布局自定义属性¶
Warning
The current page still doesn't have a translation for this language.
You can read it through google translate.
SOUI 是一套基于 XML 布局的客户端开发框架。XML 最强大的功能之一就是可以配置各种属性,这为应用程序提供了极大的灵活性。在实际开发中,用户可能需要通过在 XML 中定义自定义属性来扩展业务逻辑。
应用场景¶
在某些特定情况下,通过 XML 自定义属性可以简化业务逻辑的实现。例如,在启程输入法项目中,不同皮肤对高分屏的支持情况不同:
- 有些皮肤支持高分屏显示
- 有些皮肤不支持高分屏显示
为了统一代码逻辑处理,可以在 XML 中定义一个是否支持高分屏的属性,然后在业务代码中根据这个属性值来决定是否启用高分屏支持功能。
实现原理¶
SOUI 的布局都是以 <SOUI>
节点作为根节点开始的。用户可以在 <SOUI>
节点下配置任意非内建节点,并在这些节点中定义自定义属性。
核心实现原理参见 SHostWnd::InitFromXml
方法:
BOOL SHostWnd::InitFromXml(SXmlNode xmlNode){
// 处理用户自定义 XML 节点
SXmlNode xmlChild = xmlNode.first_child();
while (xmlChild)
{
if (xmlChild.get_userdata() != 1)
{
// 调用 SHostWnd::OnUserXmlNode 这个虚函数处理
// 业务层可以重载这个函数来处理自定义节点
OnUserXmlNode(xmlChild);
}
else
{
xmlChild.set_userdata(0);
}
xmlChild = xmlChild.next_sibling();
}
}
当 XML 解析器遇到用户自定义节点时,会调用 OnUserXmlNode
虚函数,开发者可以重载此函数来处理自定义节点和属性。
使用方法¶
1. 在 XML 中定义自定义属性¶
在 XML 布局文件中,通过 <ud>
节点定义自定义属性:
<SOUI>
<ud>
<attr name="isHighDpi" value="true" />
<attr name="skinVersion" value="2.0" />
<attr name="enableAnimation" value="false" />
</ud>
<root>
<!-- 其他布局内容 -->
</root>
</SOUI>
2. 在代码中处理和获取属性值¶
创建自定义窗口类并重载 OnUserXmlNode
方法来处理自定义属性:
class CMyHostWnd : public SHostWnd
{
private:
// 用于存储自定义属性的映射表
SMap<SStringW, SStringW> m_mapAttr;
public:
// 重载 OnUserXmlNode 方法处理自定义节点
virtual void OnUserXmlNode(SXmlNode xmlNode) override
{
// 检查节点名称是否为 "ud"
if(wcscmp(xmlNode.name(), L"ud") == 0)
{
// 处理自定义属性节点
SXmlNode xmlAttr = xmlNode.first_child();
while (xmlAttr)
{
// 检查子节点是否为 "attr" 属性节点
if (wcscmp(xmlAttr.name(), L"attr") == 0)
{
// 获取属性名
LPCWSTR pszName = xmlAttr.attribute(L"name").as_string();
// 获取属性值
LPCWSTR pszValue = xmlAttr.attribute(L"value").as_string();
// 保存属性信息到映射表中
m_mapAttr[pszName] = pszValue;
// 可以在这里添加针对特定属性的处理逻辑
ProcessCustomAttribute(pszName, pszValue);
}
xmlAttr = xmlAttr.next_sibling();
}
}
}
private:
// 处理特定自定义属性的辅助方法
void ProcessCustomAttribute(LPCWSTR pszName, LPCWSTR pszValue)
{
// 根据属性名处理不同的业务逻辑
if (wcscmp(pszName, L"isHighDpi") == 0)
{
BOOL bHighDpi = wcscmp(pszValue, L"true") == 0;
// 根据属性值启用或禁用高分屏支持
EnableHighDpiSupport(bHighDpi);
}
else if (wcscmp(pszName, L"enableAnimation") == 0)
{
BOOL bEnable = wcscmp(pszValue, L"true") == 0;
// 根据属性值启用或禁用动画效果
SetAnimationEnabled(bEnable);
}
// 可以继续添加其他属性的处理逻辑
}
// 启用高分屏支持
void EnableHighDpiSupport(BOOL bEnable)
{
// 实现高分屏支持的具体逻辑
}
// 设置动画效果启用状态
void SetAnimationEnabled(BOOL bEnable)
{
// 实现动画效果启用/禁用的具体逻辑
}
public:
// 提供获取自定义属性值的公共方法
BOOL GetCustomAttribute(LPCWSTR pszName, SStringW& strValue) const
{
if (auto it = m_mapAttr.Lookup(pszName))
{
strValue = it->Value();
return TRUE;
}
return FALSE;
}
// 提供获取布尔型属性值的便捷方法
BOOL GetCustomBoolAttribute(LPCWSTR pszName, BOOL bDefault = FALSE) const
{
SStringW strValue;
if (GetCustomAttribute(pszName, strValue))
{
return (wcscmp(strValue, L"true") == 0);
}
return bDefault;
}
};
最佳实践¶
-
命名规范:使用清晰、具有描述性的属性名称,遵循项目统一的命名规范
-
默认值处理:为自定义属性提供合理的默认值,确保在未定义属性时程序仍能正常运行
-
类型安全:提供类型安全的属性获取方法,如
GetCustomBoolAttribute
等 -
错误处理:添加适当的错误处理机制,防止因属性值格式不正确导致程序异常
-
文档注释:为自定义属性添加详细的注释说明,包括属性用途、取值范围、默认值等
-
性能考虑:避免在高频调用的场景中重复解析属性值,可以缓存解析结果
注意事项¶
-
节点名称:自定义属性节点建议使用
<ud>
标签,但这不是强制要求,可以根据需要自定义 -
属性格式:每个自定义属性必须包含
name
和value
两个属性 -
字符编码:注意处理 Unicode 字符串,确保属性值的正确解析
-
内存管理:合理管理存储属性值的容器,避免内存泄漏
通过合理使用自定义属性功能,可以显著提高 SOUI 应用的灵活性和可配置性,使业务逻辑更加清晰和易于维护。