ArkUI TextPicker 组件完整知识库
版本: 1.0 更新时间: 2026-02-02 基于: OpenHarmony ace_engine (master 分支)
目录
1. 架构概述
1.1 系统架构
TextPicker 组件实现了一个文本选择器,支持单列和多列级联,采用 4 层架构:
ArkTS 前端
↓
桥接层 (TextPickerModifier) - 属性解析和应用
↓
行模式层 (TextPickerPattern) - 业务逻辑和多列管理
↓
列模式层 (TextPickerColumnPattern) - 单列滚动逻辑
↓
滚动层 (ContainerPicker) - 滚动和吸附
设计原则:
- 多列支持: 支持单列和多列级联选择
- 自定义范围: 灵活的范围定义 (start/end)
- 级联联动: 父子列自动联动更新
- 丰富样式: 支持渐变、分割线等自定义样式
1.2 选择器类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 单列 | 单一文本选择 | 省份选择 |
| 多列 | 多列独立选择 | 年月日独立选择 |
| 级联 | 多列联动选择 | 省-市-区级联 |
2. 核心类详解
2.1 TextPickerPattern
职责:
- 管理多列数据
- 处理级联联动
- 管理样式配置
- 协调列之间的交互
关键成员变量:
// 选项数据
std::vector<NG::RangeContent> range_; // 单列选项
std::vector<NG::RangeContent> options_; // 单列选项 (别名)
std::vector<NG::TextCascadePickerOptions> cascadeOptions_; // 级联选项
std::map<WeakPtr<FrameNode>, std::vector<NG::RangeContent>> optionsWithNode_;
// 选择状态
uint32_t selectedIndex_ = 0; // 单列选中索引
std::vector<uint32_t> selecteds_; // 多列选中索引
std::vector<std::string> values_; // 选中值
// 列配置
uint32_t columnsKind_ = 0; // 列数量
std::vector<Dimension> columnWidths_; // 列宽
bool isCascade_ = false; // 是否级联
// 样式
Color backgroundColor_ = Color::WHITE;
ItemDivider divider_;
Dimension gradientHeight_;
Dimension dividerSpacing_;
PickerTextProperties textProperties_;
// 对话框按钮
WeakPtr<FrameNode> weakButtonConfirm_;
WeakPtr<FrameNode> weakButtonCancel_;
关键方法:
-
OnModifyDone(): 初始化组件
void TextPickerPattern::OnModifyDone() { LinearLayoutPattern::OnModifyDone(); // 构建列 OnColumnsBuilding(); // 初始化事件 InitOnKeyEvent(GetFocusHub()); InitFocusEvent(); } -
OnColumnsBuilding(): 构建列
void TextPickerPattern::OnColumnsBuilding() { if (isCascade_) { OnColumnsBuildingCascade(); } else { OnColumnsBuildingUnCascade(); } } -
OnColumnsBuildingUnCascade(): 构建非级联列
void TextPickerPattern::OnColumnsBuildingUnCascade() { // 单列模式 if (columnsKind_ == 1) { auto column = GetColumnNode(); FillColumnOptions(column, options_); SetSelected(selectedIndex_); return; } // 多列模式 for (uint32_t i = 0; i < columnsKind_; ++i) { auto column = CreateColumnNode(i); std::vector<NG::RangeContent> columnOptions; // 为每列填充选项 for (const auto& item : range_) { columnOptions.emplace_back(item); } FillColumnOptions(column, columnOptions); SetColumnSelected(column, selecteds_[i]); } } -
OnColumnsBuildingCascade(): 构建级联列
void TextPickerPattern::OnColumnsBuildingCascade() { // 获取根级选项 std::vector<NG::TextCascadePickerOptions> rootOptions = cascadeOptions_; // 第一列 auto firstColumn = CreateColumnNode(0); FillColumnOptions(firstColumn, rootOptions); SetColumnSelected(firstColumn, selecteds_[0]); // 后续列根据第一列的选择动态生成 UpdateCascadeColumns(0, rootOptions[selecteds_[0]]); } -
HandleColumnChange(): 处理列变化
void TextPickerPattern::HandleColumnChange( const RefPtr<FrameNode>& tag, bool isAdd, uint32_t index, bool needNotify) { if (isCascade_) { // 级联模式:更新后续列 auto columnPattern = tag->GetPattern<TextPickerColumnPattern>(); auto parentOption = columnPattern->GetCascadeOption(index); UpdateCascadeColumns(tag->GetId(), parentOption); } if (needNotify) { FireChangeEvent(true); } }
2.2 TextPickerColumnPattern
职责:
- 管理单列选项
- 处理列内滚动
- 触发变化事件
关键方法:
class TextPickerColumnPattern : public Pattern {
public:
void SetOptions(const std::vector<NG::RangeContent>& options);
void SetSelected(uint32_t index);
uint32_t GetSelected() const;
std::string GetText() const; // 获取选中文本
// 级联选项
void SetCascadeOptions(const std::vector<NG::TextCascadePickerOptions>& options);
NG::TextCascadePickerOptions GetCascadeOption(uint32_t index);
void OnColumnChange(bool isAdd, uint32_t index, bool needNotify);
};
2.3 RangeContent
struct RangeContent {
std::string text_; // 显示文本
std::string icon_; // 图标 (可选)
std::string value_; // 实际值 (可选)
};
2.4 TextCascadePickerOptions
struct TextCascadePickerOptions {
std::string text_; // 显示文本
uint32_t parentValue_; // 父级值
std::vector<TextCascadePickerOptions> children_; // 子选项
};
3. 完整交互流程
3.1 单列选择流程
用户滚动选项列
↓
TextPickerColumnPattern::OnColumnChange()
↓
更新 selectedIndex_
↓
FireChangeEvent(true)
↓
构建返回数据:
GetSelectedObject()
↓
触发 ArkTS onChange 回调
3.2 级联选择流程
用户滚动第一列
↓
TextPickerColumnPattern::OnColumnChange()
↓
HandleColumnChange(tag, isAdd, index, needNotify)
↓
获取选中的父选项
↓
UpdateCascadeColumns(parentId, parentOption)
↓
重新生成后续列:
- 清空子列选项
- 根据 parentOption.children_ 填充
- 重置子列选中索引到 0
↓
FireChangeEvent(true)
↓
构建级联数据返回
4. 关键技术点
4.1 级联选项处理
void TextPickerPattern::UpdateCascadeColumns(
int32_t parentColumnId,
const NG::TextCascadePickerOptions& parentOption)
{
auto host = GetHost();
uint32_t columnIndex = GetColumnIndex(parentColumnId);
// 遍历后续列
for (uint32_t i = columnIndex + 1; i < columnsKind_; ++i) {
auto column = GetColumnNode(i);
if (i == columnIndex + 1) {
// 下一列:使用父选项的子项
if (!parentOption.children_.empty()) {
FillCascadeColumnOptions(column, parentOption.children_);
SetColumnSelected(column, 0);
} else {
ClearColumnOptions(column);
}
} else {
// 更后续列:清空
ClearColumnOptions(column);
}
}
}
void TextPickerPattern::ProcessCascadeOptions(
const std::vector<NG::TextCascadePickerOptions>& options,
std::vector<NG::TextCascadePickerOptions>& reOptions,
uint32_t index)
{
// 处理级联选项数据
for (const auto& option : options) {
NG::TextCascadePickerOptions newOption;
newOption.text_ = option.text_;
newOption.parentValue_ = index;
// 递归处理子选项
if (!option.children_.empty()) {
ProcessCascadeOptions(option.children_, newOption.children_, ...);
}
reOptions.emplace_back(newOption);
}
}
4.2 范围解析
// 类型: RANGE_TYPE_INT (整数范围)
TextPicker({
range: ['2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018',
'2019', '2020', '2021', '2022', '2023', '2024']
})
.rangeType(RangeType.RANGE_TYPE_INT)
// 解析逻辑
void TextPickerPattern::ParseRangeResult(NG::TextCascadePickerOptions& option) {
std::vector<std::string> rangeResultValue;
// 从 resource 解析
if (rangeType_ == RANGE_TYPE_INT) {
// 整数范围: [0, 100] → "0", "1", "2", ..., "100"
for (int32_t i = rangeStart_; i <= rangeEnd_; ++i) {
rangeResultValue.emplace_back(std::to_string(i));
}
} else if (rangeType_ == RANGE_TYPE_FLOAT) {
// 浮点数范围
double value = rangeStart_;
while (value <= rangeEnd_) {
rangeResultValue.emplace_back(std::to_string(value));
value += rangeStep_;
}
} else if (rangeType_ == RANGE_TYPE_STRING) {
// 字符串数组
rangeResultValue = rangeValues_;
}
// 填充选项
for (const auto& value : rangeResultValue) {
NG::RangeContent content;
content.text_ = value;
options_.emplace_back(content);
}
}
4.3 选项获取
std::string TextPickerPattern::GetOption(uint32_t index) const {
if (index >= GetOptionCount()) {
return "";
}
return options_[index].text_;
}
std::string TextPickerPattern::GetSelectedObject(
bool isColumnChange, int32_t status, bool isEnterSelectedAreaEvent) const
{
if (isCascade_) {
return GetSelectedObjectMulti(values_, selecteds_, status);
}
// 单列模式
return std::to_string(selectedIndex_);
}
std::string TextPickerPattern::GetSelectedObjectMulti(
const std::vector<std::string>& values,
const std::vector<uint32_t>& indexs,
int32_t status) const
{
// 构建多列选择结果
std::string result = "{";
for (size_t i = 0; i < values.size(); ++i) {
result += "\"" + std::to_string(i) + "\":\"" + values[i] + "\"";
if (i < values.size() - 1) {
result += ",";
}
}
result += "}";
return result;
}
4.4 自定义样式
void TextPickerPattern::SetTextProperties(const PickerTextProperties& properties) {
textProperties_ = properties;
// 消失文本样式
if (properties.disappearTextStyle_.fontSize.has_value()) {
gradientHeight_ = properties.disappearTextStyle_.fontSize.value();
}
// 普通文本样式
if (properties.normalTextStyle_.fontSize.has_value()) {
gradientHeight_ = std::max(properties.normalTextStyle_.fontSize.value(),
gradientHeight_);
}
// 选中文本样式
if (properties.selectedTextStyle_.fontSize.has_value()) {
dividerSpacing_ = properties.selectedTextStyle_.fontSize.value();
}
}
4.5 数字表冠支持
#ifdef SUPPORT_DIGITAL_CROWN
void TextPickerPattern::InitOnCrownEvent(const RefPtr<FocusHub>& focusHub) {
auto crownEvent = [weak = WeakClaim(this)](const CrownEvent& event) {
auto pattern = weak.Upgrade();
CHECK_NULL_VOID(pattern);
if (!pattern->haveFocus_) {
return false;
}
// 根据旋转方向调整选中值
if (event.rotation > 0) {
pattern->ShowNext();
} else {
pattern->ShowPrevious();
}
return true;
};
focusHub->SetOnCaptureCrownEvent(crownEvent);
}
#endif
5. 属性系统
5.1 布局属性 (TextPickerLayoutProperty)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| range | vector<RangeContent> | - | 选项范围 |
| selected | uint32_t | 0 | 选中索引 |
| value | string | - | 选中值 |
| columnsKind | uint32_t | 1 | 列数量 |
| columnWidths | vector<Dimension> | - | 列宽 |
| isCascade | bool | false | 是否级联 |
| canLoop | bool | true | 是否循环 |
5.2 样式属性
| 属性 | 类型 | 说明 |
|---|---|---|
| backgroundColor | Color | 背景颜色 |
| divider | ItemDivider | 分割线样式 |
| gradientHeight | Dimension | 渐变高度 |
| dividerSpacing | Dimension | 分割线间距 |
| textProperties | PickerTextProperties | 文本样式 |
5.3 PickerTextProperties
struct PickerTextProperties {
PickerTextStyle disappearTextStyle_; // 消失文本样式
PickerTextStyle normalTextStyle_; // 普通文本样式
PickerTextStyle selectedTextStyle_; // 选中文本样式
};
struct PickerTextStyle {
std::optional<Color> color;
std::optional<Dimension> fontSize;
std::optional<std::vector<std::string>> fontFamily;
std::optional<Dimension> minFontSize;
std::optional<Dimension> maxFontSize;
};
6. 事件系统
6.1 事件类型
onChange - 选择改变:
void TextPickerPattern::FireChangeEvent(bool refresh) {
auto eventHub = GetEventHub<TextPickerEventHub>();
auto onChange = eventHub->GetChangeEvent();
if (onChange) {
auto info = GetSelectedObject(false);
onChange(info); // 触发 ArkTS onChange 回调
}
}
onScrollStop - 滚动停止:
void TextPickerPattern::FireScrollStopEvent(bool refresh) {
auto eventHub = GetEventHub<TextPickerEventHub>();
auto onScrollStop = eventHub->GetScrollStopEvent();
if (onScrollStop) {
onScrollStop(selectedIndex_);
}
}
onEnterSelectedArea - 进入选中区域:
void TextPickerPattern::FireEnterSelectedAreaEvent(bool refresh) {
auto eventHub = GetEventHub<TextPickerEventHub>();
auto onEnterSelectedArea = eventHub->GetEnterSelectedAreaEvent();
if (onEnterSelectedArea) {
onEnterSelectedArea(true);
}
}
7. 级联选项
7.1 级联数据结构
// 省市区级联示例
TextPicker({
cascadeOptions: [
{
text: '广东省',
parentValue: 0,
children: [
{
text: '广州市',
parentValue: 0,
children: [
{ text: '天河区', parentValue: 0 },
{ text: '越秀区', parentValue: 1 },
// ...
]
},
{
text: '深圳市',
parentValue: 1,
children: [
{ text: '南山区', parentValue: 0 },
{ text: '福田区', parentValue: 1 },
// ...
]
}
]
},
// ... 其他省份
]
})
7.2 级联更新流程
第一列选择 "广东省"
↓
HandleColumnChange(firstColumn, false, selectedIndex, true)
↓
获取 cascadeOptions_[selectedIndex]
↓
更新第二列:
- 清空旧选项
- 填充 children_ (广州市, 深圳市, ...)
- 选中第一个 (广州市)
↓
更新第三列:
- 清空旧选项
- 填充 广州市.children_ (天河区, 越秀区, ...)
- 选中第一个 (天河区)
8. 自定义样式
8.1 渐变效果
// PaintMethod 绘制渐变
void TextPickerPaintMethod::PaintGradient(PaintWrapper* paintWrapper) {
auto canvas = paintWrapper->GetCanvas();
// 上方渐变 (消失文本)
auto gradient1 = canvas->CreateLinearGradient(0, 0, 0, gradientHeight_);
gradient1->AddColorStop(0.0, Color::TRANSPARENT);
gradient1->AddColorStop(1.0, Color::WHITE);
// 下方渐变
auto gradient2 = canvas->CreateLinearGradient(0, height - gradientHeight_,
0, height);
gradient2->AddColorStop(0.0, Color::WHITE);
gradient2->AddColorStop(1.0, Color::TRANSPARENT);
// 绘制渐变
canvas->DrawRect(0, 0, width, gradientHeight_, gradient1);
canvas->DrawRect(0, height - gradientHeight_, width, gradientHeight_, gradient2);
}
8.2 分割线样式
struct ItemDivider {
bool startMargin = false;
bool endMargin = false;
Dimension strokeWidth = 2.0_vp;
Color color = Color::GRAY;
Dimension marginTop = 0.0_vp;
Dimension marginBottom = 0.0_vp;
};
// 绘制分割线
void TextPickerPaintMethod::PaintDivider(PaintWrapper* paintWrapper) {
if (!divider_.strokeWidth.IsValid()) {
return;
}
auto canvas = paintWrapper->GetCanvas();
Paint paint;
paint.SetAntiAlias(true);
// 上分割线
float topY = (pickerHeight - dividerSpacing) / 2;
canvas->DrawLine(offsetX, topY, offsetX + width, topY, paint);
// 下分割线
float bottomY = topY + dividerSpacing;
canvas->DrawLine(offsetX, bottomY, offsetX + width, bottomY, paint);
}
9. 最佳实践
9.1 基本使用
推荐做法:
// 1. 单列选择
TextPicker({ range: ['A', 'B', 'C', 'D', 'E'] })
.selected(0)
.onChange((value: string) => {
console.log('Selected:', value);
})
// 2. 多列独立选择
TextPicker({
range: [
['2010', '2011', '2012'],
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
['1', '2', '3']
]
})
.columnsKind(3)
// 3. 级联选择
TextPicker({
cascadeOptions: provinceCityData
})
.isCascade(true)
9.2 样式定制
// 自定义文本样式
TextPicker()
.textStyle({
disappearTextStyle: { color: '#CCCCCC', fontSize: 14 },
normalTextStyle: { color: '#333333', fontSize: 16 },
selectedTextStyle: { color: '#FF0000', fontSize: 18 }
})
// 自定义分割线
TextPicker()
.divider({
strokeWidth: 2,
color: Color.Blue,
startMargin: true,
endMargin: true
})
// 自定义渐变
TextPicker()
.gradientHeight(50)
.dividerSpacing(40)
9.3 性能优化
// 1. 限制选项数量
TextPicker()
.range(items.slice(0, 100))
// 2. 使用虚拟滚动
// 内部自动实现
10. 问题排查
10.1 常见问题
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 级联不更新 | children_ 为空 | 检查级联数据结构 |
| 样式不生效 | 属性未正确设置 | 检查属性类型和值 |
| 多列显示异常 | columnsKind 配置错误 | 检查列数和选项数量 |
| 选项无法选择 | 超出范围 | 检查 selected 索引 |
10.2 调试工具
日志输出:
# 查看文本选择器相关日志
hilog -T ArkUI | grep -i textpicker
相关目录
frameworks/core/components_ng/pattern/text_picker/
├── textpicker_pattern.h/cpp # 主要逻辑
├── textpicker_column_pattern.h/cpp # 列模式
├── textpicker_layout_property.h # 布局属性
├── textpicker_event_hub.h # 事件处理
├── textpicker_paint_method.h/cpp # 绘制方法
├── textpicker_dialog_view.h/cpp # 对话框视图
├── textpicker_accessibility_property.h/cpp # 无障碍属性
├── toss_animation_controller.h/cpp # 动画控制
├── textpicker_properties.h # 属性定义
└── textpicker_overscroll.h/cpp # 滚动边界处理
关键要点总结
- 多列支持: 支持单列和多列独立选择
- 级联联动: 父子列自动联动更新选项
- 自定义范围: 灵活的范围定义 (整数/浮点/字符串)
- 丰富样式: 渐变、分割线、文本样式全面支持
- 数字表冠: 支持手表设备数字表冠操作
- 触觉反馈: 滚动时提供震动反馈
- 对话框支持: 可在对话框中显示
- 无障碍: 完整的键盘导航和焦点管理