Skip to content

布局自定义属性

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;
    }
};

最佳实践

  1. 命名规范:使用清晰、具有描述性的属性名称,遵循项目统一的命名规范

  2. 默认值处理:为自定义属性提供合理的默认值,确保在未定义属性时程序仍能正常运行

  3. 类型安全:提供类型安全的属性获取方法,如 GetCustomBoolAttribute

  4. 错误处理:添加适当的错误处理机制,防止因属性值格式不正确导致程序异常

  5. 文档注释:为自定义属性添加详细的注释说明,包括属性用途、取值范围、默认值等

  6. 性能考虑:避免在高频调用的场景中重复解析属性值,可以缓存解析结果

注意事项

  1. 节点名称:自定义属性节点建议使用 <ud> 标签,但这不是强制要求,可以根据需要自定义

  2. 属性格式:每个自定义属性必须包含 namevalue 两个属性

  3. 字符编码:注意处理 Unicode 字符串,确保属性值的正确解析

  4. 内存管理:合理管理存储属性值的容器,避免内存泄漏

通过合理使用自定义属性功能,可以显著提高 SOUI 应用的灵活性和可配置性,使业务逻辑更加清晰和易于维护。