​Select组件存在多个时无法独立设置单个样式问题分析报告

鸿蒙小青娃 2023-03-14 09:20:34

1 关键字

Select;样式

2 问题描述

页面有2个select组件,对其中某个Select设置了样式,另外一个没有设置样式的Select的样式也改变了,效果类同于两个Select统一设置了样式。

复现问题的代码如下(3.1release及截至当前[2022/10/20] master版本可复现):

@Entry
@Component
struct SelectExample {
  build() {
    Column() {
      Select([{value:'aaa'},
              {value:'bbb'}]).value('aaa')

      Select([{value:'aaa'},
              {value:'bbb'}]).value('aaa')
        .fontColor(Color.Red)
    }
  }
}

代码上只有第二个Select设置绿色字体,实际显示两个Select都是绿色字体

3 问题原因

3.1 正常机制

Select组件存在多个时,每个Select都可以独立设置自己的样式

3.2 异常机制

Select组件存在多个时,每个Select无法独立设置自己的样式

3.3 原因分析

应用渲染时,每个Select组件会执行 Select.create ,对应c++ 侧源码frameworks\bridge\declarative_frontend\jsview\js_select.cpp

void JSSelect::Create(const JSCallbackInfo& info)
{
    ...
    //生成 selectComponent以及对应的'样式'配置Theme
    auto selectTheme = GetTheme<SelectTheme>(); 
    auto selectComponent = AceType::MakeRefPtr<SelectComponent>();
    selectComponent->SetTheme(selectTheme);
    ...
}

其中 GetTheme的源码位于js_view_abstract.h

// T为SelectTheme,调用themeManager取得selectTheme
static RefPtr<T> GetTheme()
{
    ...
    auto themeManager = pipelineContext->GetThemeManager();
    ...
    return themeManager->GetTheme<T>();
}

themeManager的GetTheme源码如下(frameworks\core\components\theme\theme_manager.cpp):
首先在名称为themes_的unordered_map里根据ThemeType(SelectTheme::TypeId())查找目标theme,存在则返回目标theme,不存在则生成一个,并且存在themes_里。

RefPtr<Theme> ThemeManager::GetTheme(ThemeType type)
{
    auto findIter = themes_.find(type);
    if (findIter != themes_.end()) {
        //查到了目标theme,则直接返回
        return findIter->second;
    }
    auto builderIter = THEME_BUILDERS.find(type);
    if (builderIter == THEME_BUILDERS.end()) {
        LOGE("No theme builder defined! type=%{public}zu", type);
        return nullptr;
    }
    auto theme = builderIter->second(themeConstants_); //生成theme
    themes_.emplace(type, theme); //把theme存进themes_
    return theme;
}

如此,一个页面内多个Select存在时,第一个Select生成了theme,其后都共用这个theme。

设置样式例如设置字体颜色 Select.fontColor(Color.Green); 源码如下,

void JSSelect::FontColor(const JSCallbackInfo& info)
{
    ...
    auto component = ViewStackProcessor::GetInstance()->GetMainComponent();
    auto selectComponent = AceType::DynamicCast<SelectComponent>(component);
    if (!selectComponent) {
        return;
    }

    Color textColor;
    if (!ParseJsColor(info[0], textColor)) {
        return;
    }
    auto textStyle = selectComponent->GetSelectStyle(); //取出textStyle
    textStyle.SetTextColor(textColor); //修改textStyle
    selectComponent->SetSelectStyle(std::move(textStyle)); //重新设置textStyle
}

其中的 GetSelectStyle,SetSelectStyle依赖于对theme(来源于RefPtr ThemeManager::GetTheme)的操作

const TextStyle& GetSelectStyle() const
{
    return theme_->GetTitleStyle();
}

void SetSelectStyle(const TextStyle& style)
{
    theme_->SetTitleStyle(style);
}

即对于同一个样式属性,存在多个Select组件时,最后一个带有此属性设置Select的解析影响最终的theme,此theme决定了Select的样式,表现出来就是Select组件无法设置单个组件样式,设置单个的Select组件样式会影响到其他Select组件。

4 解决方案

修改 JSSelect::Create函数内 auto selectTheme = GetTheme<SelectTheme>();

auto selectTheme = GetTheme<SelectTheme>()->clone();
即每一个Select组件对应一个独立的(来自于clone方法)theme。

5 定位过程

从现象上看很类似于'共享一份样式',从源码上着重看样式设置的逻辑。

...全文
7 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

513

社区成员

发帖
与我相关
我的任务
社区描述
OpenHarmony开发者社区
其他 企业社区
社区管理员
  • csdnsqst0025
  • shewaliujingli
  • BaoWei
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧