使用滑动选择器 (Picker)
================

#### 概述

从API version 23开始，ArkUI开发框架在NDK接口提供了Picker容器组件。Picker容器组件用于实现用户自定义选项的选择操作，支持滚动选择、触感反馈、循环滚动等功能。Picker组件通过设置选择指示器样式，可以自定义选中项的显示效果，适用于日期选择、时间选择、文本选择等场景。

[创建Picker](#创建picker)后，可以[设置Picker属性](#设置picker属性)，并[监听Picker事件](#监听picker事件)。

使用NDK接口构建UI界面以及NDK基本使用，可以参考[接入ArkTS页面](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ndk-access-the-arkts-page)。

#### 创建Picker

通过调用[createNode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#createnode)()得到组件对象指针，节点类型为ARKUI\_NODE\_PICKER，并设置[ArkUI\_NodeAttributeType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#arkui_nodeattributetype)中的相关属性，创建一个Picker容器组件。

#### \[h2\]基本创建方式

以下示例展示了创建Picker组件并设置基本属性的方法。

static ArkUI\_NodeHandle CreatePicker(ArkUI\_NativeNodeAPI\_1 \*api)
{
    ArkUI\_NodeHandle picker = api->createNode(ARKUI\_NODE\_PICKER);
    if (picker == nullptr) {
        return nullptr;
    }
    if (g\_state) {
        g\_state->pickerNode = picker;
    }
    ArkUI\_NumberValue widthValue = {.f32 = K\_PICKER\_WIDTH\_RATIO};
    ArkUI\_AttributeItem widthItem = {&widthValue, sizeof(widthValue) / sizeof(ArkUI\_NumberValue)};
    api->setAttribute(picker, NODE\_WIDTH\_PERCENT, &widthItem);
    UpdatePickerSelectedIndex();
    SetDisplayedItemCount(K\_VISIBLE\_COUNT);
    SetItemHeight(K\_ITEM\_HEIGHT);
    api->registerNodeEvent(picker, NODE\_PICKER\_EVENT\_ON\_CHANGE, K\_ON\_CHANGE\_EVENT\_ID, nullptr);
    api->registerNodeEvent(picker, NODE\_PICKER\_EVENT\_ON\_SCROLL\_STOP, K\_ON\_SCROLL\_STOP\_EVENT\_ID, nullptr);
    if (g\_state) {
        for (const auto &item : g\_state->dataArray) {
            ArkUI\_NodeHandle optionNode = CreatePickerOption(api, item);
            if (optionNode != nullptr) {
                api->addChild(picker, optionNode);
            }
        }
    }
    return picker;
}

#### \[h2\]封装为工具类

参考[示例](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ndk-access-the-arkts-page#示例)中列表组件的实现方式，可以将Picker组件常用的属性设置封装到自定义的工具类中方便后续使用。

class ContainerPickerCanLoopMaker : public BaseNode {
public:
    static ArkUI\_NodeHandle CreateNativeNode();

    ContainerPickerCanLoopMaker()
        : BaseNode(NodeApiInstance::GetInstance()->GetNativeNodeAPI()->createNode(ARKUI\_NODE\_PICKER)),
          nodeApi\_(NodeApiInstance::GetInstance()->GetNativeNodeAPI())
    {
        if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
            return;
        }
    }

    ~ContainerPickerCanLoopMaker() override = default;

    // ========================================
    // 基础尺寸设置接口
    // ========================================
    void SetPickerSize(float width, float height) { SetSize(width, height); }

    void SetPickerSizePercent(float widthPercent, float heightPercent) { SetSizePercent(widthPercent, heightPercent); }

    void SetPickerWidthPercent(float widthPercent)
    {
        SetAttributeFloat32(nodeApi\_, GetHandle(), NODE\_WIDTH\_PERCENT, widthPercent);
    }

    void SetPickerHeightPercent(float heightPercent)
    {
        SetAttributeFloat32(nodeApi\_, GetHandle(), NODE\_HEIGHT\_PERCENT, heightPercent);
    }

    // ========================================
    // Picker特有属性设置接口
    // ========================================
    void SetSelectedIndex(uint32\_t index)
    {
        if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
            return;
        }
        ArkUI\_NumberValue selectedIndexValue = {.u32 = index};
        ArkUI\_AttributeItem selectedIndexItem = {&selectedIndexValue,
                                                 sizeof(selectedIndexValue) / sizeof(ArkUI\_NumberValue)};
        nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_OPTION\_SELECTED\_INDEX, &selectedIndexItem);
    }

    void SetCanLoop(bool canLoop)
    {
        if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
            return;
        }
        ArkUI\_NumberValue canLoopValue = {.i32 = canLoop ? 1 : 0};
        ArkUI\_AttributeItem canLoopItem = {&canLoopValue, sizeof(canLoopValue) / sizeof(ArkUI\_NumberValue)};
        nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_CAN\_LOOP, &canLoopItem);
    }

    void SetHapticFeedback(bool enabled)
    {
        if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
            return;
        }
        ArkUI\_NumberValue enableHapticFeedbackValue = {.i32 = enabled ? 1 : 0};
        ArkUI\_AttributeItem enableHapticFeedbackItem = {&enableHapticFeedbackValue,
                                                        sizeof(enableHapticFeedbackValue) / sizeof(ArkUI\_NumberValue)};
        nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_ENABLE\_HAPTIC\_FEEDBACK, &enableHapticFeedbackItem);
    }

    void SetSelectionIndicatorBackground(uint32\_t backgroundColor, float cornerRadius = 10.0f)
    {
        if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
            return;
        }
        ArkUI\_PickerIndicatorStyle \*indicatorStyle =
            OH\_ArkUI\_PickerIndicatorStyle\_Create(ARKUI\_PICKER\_INDICATOR\_BACKGROUND);
        if (indicatorStyle == nullptr) {
            return;
        }
        ArkUI\_PickerIndicatorBackground background = {.backgroundColor = backgroundColor,
                                                      .topLeftRadius = cornerRadius,
                                                      .topRightRadius = cornerRadius,
                                                      .bottomLeftRadius = cornerRadius,
                                                      .bottomRightRadius = cornerRadius};
        OH\_ArkUI\_PickerIndicatorStyle\_ConfigureBackground(indicatorStyle, &background);
        ArkUI\_AttributeItem selectionIndicatorItem = {.object = indicatorStyle};
        nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_SELECTION\_INDICATOR, &selectionIndicatorItem);
    }

    void SetSelectionIndicatorDivider(uint32\_t dividerColor, float strokeWidth = 2.0f, float startMargin = 20.0f,
                                      float endMargin = 20.0f)
    {
        if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
            return;
        }
        ArkUI\_PickerIndicatorStyle \*indicatorStyle =
            OH\_ArkUI\_PickerIndicatorStyle\_Create(ARKUI\_PICKER\_INDICATOR\_DIVIDER);
        if (indicatorStyle == nullptr) {
            return;
        }
        ArkUI\_PickerIndicatorDivider divider = {.strokeWidth = strokeWidth,
                                                .dividerColor = dividerColor,
                                                .startMargin = startMargin,
                                                .endMargin = endMargin};
        OH\_ArkUI\_PickerIndicatorStyle\_ConfigureDivider(indicatorStyle, &divider);
        ArkUI\_AttributeItem selectionIndicatorItem = {.object = indicatorStyle};
        nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_SELECTION\_INDICATOR, &selectionIndicatorItem);
    }

    // ========================================
    // 公共辅助方法
    // ========================================
    ArkUI\_NativeNodeAPI\_1 \*GetNodeAPI() const { return nodeApi\_; }

protected:
    void OnNodeEvent(ArkUI\_NodeEvent \*event) override { BaseNode::OnNodeEvent(event); }

private:
    ArkUI\_NativeNodeAPI\_1 \*nodeApi\_ = nullptr;
};

#### 设置Picker属性

#### \[h2\]设置默认选中项

通过设置[ArkUI\_NodeAttributeType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#arkui_nodeattributetype)中的NODE\_PICKER\_OPTION\_SELECTED\_INDEX属性，可以设置Picker组件的默认选中项索引。

ArkUI\_NumberValue selectedIndexValue = {.u32 = index};
ArkUI\_AttributeItem selectedIndexItem = {&selectedIndexValue,
                                         sizeof(selectedIndexValue) / sizeof(ArkUI\_NumberValue)};
nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_OPTION\_SELECTED\_INDEX, &selectedIndexItem);

#### \[h2\]设置触感反馈

通过设置NODE\_PICKER\_ENABLE\_HAPTIC\_FEEDBACK属性，可以控制Picker组件是否启用触感反馈。开启后，当用户滚动选择器时，如果系统硬件支持，会产生触感反馈。

使用[ArkUI\_NativeNodeAPI\_1](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1)时可直接调用setAttribute。

ArkUI\_NumberValue enableHapticFeedbackValue = {.i32 = enabled ? 1 : 0};
ArkUI\_AttributeItem enableHapticFeedbackItem = {&enableHapticFeedbackValue,
                                                sizeof(enableHapticFeedbackValue) / sizeof(ArkUI\_NumberValue)};
nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_ENABLE\_HAPTIC\_FEEDBACK, &enableHapticFeedbackItem);

若使用上文封装的ContainerPickerCanLoopMaker，可调用已封装的接口。

picker->SetHapticFeedback(K\_HAPTIC\_FEEDBACK);

#### \[h2\]设置循环滚动

通过设置NODE\_PICKER\_CAN\_LOOP属性，可以控制Picker组件是否支持循环滚动。设置为true时，选择器可以无限循环滚动；设置为false时，滚动到首尾时会停止。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/a0/v3/mu8gapYrQUagh-2v_RXIMw/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132315Z&HW-CC-Expire=86400&HW-CC-Sign=01A4EB67CFFD2795E7C7685DD38223797083D0D392116FF0969F12EF5FE7E6D3)

如果子组件的个数小于8个，无论设置为true还是false，都不会循环滚动。

使用[ArkUI\_NativeNodeAPI\_1](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1)时可直接调用setAttribute。

ArkUI\_NumberValue canLoopValue = {.i32 = canLoop ? 1 : 0};
ArkUI\_AttributeItem canLoopItem = {&canLoopValue, sizeof(canLoopValue) / sizeof(ArkUI\_NumberValue)};
nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_CAN\_LOOP, &canLoopItem);

若使用上文封装的ContainerPickerCanLoopMaker，可调用已封装的接口。

picker->SetCanLoop(K\_CAN\_LOOP);

#### \[h2\]设置选择指示器样式

通过设置NODE\_PICKER\_SELECTION\_INDICATOR属性，可以自定义Picker组件的选择指示器样式。选择指示器包括背景样式和分割线样式两部分。

背景样式指示器通过[ArkUI\_PickerIndicatorBackground](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-pickerindicatorbackground)结构体设置，包括背景颜色和圆角半径。

void SetSelectionIndicatorBackground(uint32\_t backgroundColor, float cornerRadius = 10.0f)
{
    if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
        return;
    }
    ArkUI\_PickerIndicatorStyle \*indicatorStyle =
        OH\_ArkUI\_PickerIndicatorStyle\_Create(ARKUI\_PICKER\_INDICATOR\_BACKGROUND);
    if (indicatorStyle == nullptr) {
        return;
    }
    ArkUI\_PickerIndicatorBackground background = {.backgroundColor = backgroundColor,
                                                  .topLeftRadius = cornerRadius,
                                                  .topRightRadius = cornerRadius,
                                                  .bottomLeftRadius = cornerRadius,
                                                  .bottomRightRadius = cornerRadius};
    OH\_ArkUI\_PickerIndicatorStyle\_ConfigureBackground(indicatorStyle, &background);
    ArkUI\_AttributeItem selectionIndicatorItem = {.object = indicatorStyle};
    nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_SELECTION\_INDICATOR, &selectionIndicatorItem);
}

分割线样式指示器通过[ArkUI\_PickerIndicatorDivider](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-pickerindicatordivider)结构体设置，包括线宽、颜色和边距。

void SetSelectionIndicatorDivider(uint32\_t dividerColor, float strokeWidth = 2.0f, float startMargin = 20.0f,
                                  float endMargin = 20.0f)
{
    if (!IsNotNull(nodeApi\_) || !IsNotNull(GetHandle())) {
        return;
    }
    ArkUI\_PickerIndicatorStyle \*indicatorStyle =
        OH\_ArkUI\_PickerIndicatorStyle\_Create(ARKUI\_PICKER\_INDICATOR\_DIVIDER);
    if (indicatorStyle == nullptr) {
        return;
    }
    ArkUI\_PickerIndicatorDivider divider = {.strokeWidth = strokeWidth,
                                            .dividerColor = dividerColor,
                                            .startMargin = startMargin,
                                            .endMargin = endMargin};
    OH\_ArkUI\_PickerIndicatorStyle\_ConfigureDivider(indicatorStyle, &divider);
    ArkUI\_AttributeItem selectionIndicatorItem = {.object = indicatorStyle};
    nodeApi\_->setAttribute(GetHandle(), NODE\_PICKER\_SELECTION\_INDICATOR, &selectionIndicatorItem);
}

将背景样式或分割线样式组合到[ArkUI\_PickerIndicatorStyle](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-pickerindicatorstyle)结构体中，并设置给Picker组件。

若使用上文封装的ContainerPickerMonthMaker，可调用已封装的接口。

picker->SetSelectionIndicatorDivider(0xFF0000FF, 2.0f, 20.0f, 20.0f);

#### 监听Picker事件

本示例通过[registerNodeEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#registernodeevent)注册Picker组件的事件类型[ArkUI\_NodeEventType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#arkui_nodeeventtype)，使用ArkUI\_NodeEventItem结构体为每个事件指定独立的回调函数。开发者无需额外注册全局回调函数，每个事件项直接绑定回调函数。Picker组件支持的事件如下：

| 枚举 | 说明 | 起始版本 |
| :-- | :-- | :-- |
| NODE\_PICKER\_EVENT\_ON\_CHANGE | Picker组件中选择某项时触发的事件。 | 23 |
| NODE\_PICKER\_EVENT\_ON\_SCROLL\_STOP | Picker组件中选择某项且滚动停止时触发的事件。 | 23 |

#### \[h2\]监听选择变化事件

通过[registerNodeEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#registernodeevent)注册NODE\_PICKER\_EVENT\_ON\_CHANGE事件，使用ArkUI\_NodeEventItem结构体指定回调函数，可以监听Picker组件的选择变化。事件回调中会返回选中项的索引值。

api->registerNodeEvent(picker, NODE\_PICKER\_EVENT\_ON\_CHANGE, K\_ON\_CHANGE\_EVENT\_ID, nullptr);

#### \[h2\]监听滚动停止事件

通过[registerNodeEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#registernodeevent)注册NODE\_PICKER\_EVENT\_ON\_SCROLL\_STOP事件，使用ArkUI\_NodeEventItem结构体指定回调函数，可以监听Picker组件滚动停止时的选择变化。与NODE\_PICKER\_EVENT\_ON\_CHANGE事件相比，该事件只在滚动停止时触发，适合需要在滚动完成后再处理选择的场景。

api->registerNodeEvent(picker, NODE\_PICKER\_EVENT\_ON\_SCROLL\_STOP, K\_ON\_SCROLL\_STOP\_EVENT\_ID, nullptr);

#### 完整示例

[Native\_Type\_Sample](https://gitcode.com/HarmonyOS_Samples/guide-snippets/tree/master/ArkUISample/NativeType/native_type_sample)