422
社区成员




Select;样式
页面有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都是绿色字体
Select组件存在多个时,每个Select都可以独立设置自己的样式
Select组件存在多个时,每个Select无法独立设置自己的样式
应用渲染时,每个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组件。
修改 JSSelect::Create函数内 auto selectTheme = GetTheme<SelectTheme>();
为
auto selectTheme = GetTheme<SelectTheme>()->clone();
即每一个Select组件对应一个独立的(来自于clone方法)theme。
从现象上看很类似于'共享一份样式',从源码上着重看样式设置的逻辑。