HarmonyOS鸿蒙NEXT中自定义组件下拉选择框

HarmonyOS鸿蒙NEXT中自定义组件下拉选择框

背景

在项目中遇到了一个下拉选择框组件的开发需求:

  • 选择框的三角箭头需要在点击时跟随转动。
  • 弹出的选择框需要基本满框弹出。
  • 选择框显示选择的项的方式为后面加一个✔的符号或图片。
  • 选择框选择不是第一个默认的时候,需要提供一个不同颜色的显示提醒用户。
  • 选择框需要提供方法,实现选择第一列的数值的时候,可以格式化成其他的文字内容。

根据测试发现,系统自带的Select组件的自定义无法满足想要的效果,因此根据多次的测试后,选择使用Toggle+bindMenu的方式,实现对组件的最大自定义封装。并提供了Option设置类,代码复制后可以直接使用。

实现效果

实现效果

代码文件

SelectDemo

调用使用页面文件

@Entry
@ComponentV2
export struct SelectDemo {
  @Local Option: ToggleMenuOption = new ToggleMenuOption()
  @Local DistanceOption: ToggleMenuOption = new ToggleMenuOption()

  aboutToAppear(): void {
    this.Option.Items = ["推荐排序", "距离最近", "评分最高"];
    this.Option.SelectValue = "推荐排序";
    this.Option.SelectedIndex = 0;
    this.DistanceOption.Items = ["不限", "500 米", "1000 米", "3000 米", "5000 米", "10000 米"];
    this.DistanceOption.SelectValue = "位置距离";
    this.DistanceOption.SelectedIndex = 0;
    this.DistanceOption.ToggleBackground = "#e6eaeb"
    this.DistanceOption.SelectValueFormatAction = (value) => {
      if (value == "不限") {
        return "位置距离";
      }
      return value;
    }
  }

  build() {
    Column({ space: 20 }) {
      ToggleMenu({ Option: this.Option })
      ToggleMenu({ Option: this.DistanceOption })
    }
    .width("100%")
    .height("100%")
  }
}

ToggleMenuOption

自定义组件配置类,提供了是否显示菜单,选中字符、菜单索引等组件内容设置,大家可以根据自己的需求修改当前类的内容,可以添加和删减

@ObservedV2
export class ToggleMenuOption {
  /**
   * 是否展示菜单
   */
  @Trace IsShowMenu: boolean = false
  /**
   * 菜单集合
   */
  @Trace Items: string[] = []
  /**
   * 选中字符串
   */
  @Trace SelectValue: string = ""
  /**
   * 菜单选择的索引
   */
  @Trace SelectIndex: number = -1
  /**
   * 选择器默认背景颜色
   */
  @Trace ToggleBackground: ResourceColor = "#f4f6f5"
  /**
   * 选择不是第一个选项时显示的背景颜色
   */
  @Trace ToggleUnFirstBackground: ResourceColor = "#c2d3f4"
  /**
   * 选择按钮高度
   */
  @Trace ToggleHeight: Length = 35
  /**
   * 选择按钮宽度
   */
  @Trace ToggleWidth: Length = 120
  /**
   * 菜单选项高度
   */
  @Trace MenuItemHeight: Length = 40
  /**
   * 菜单展示宽度
   */
  @Trace MenuWidth: Length = "80%"
  /**
   * 菜单内缩值
   */
  @Trace MenuPadding: Padding = {
    top: 10,
    bottom: 10,
    right: 20,
    left: 20
  }
  /**
   * 选择内容需要对应格式化时
   */
  @Trace SelectValueFormatAction?: (value: string) => string
  /**
   * 菜单点击切换方法
   */
  @Trace SelectChangeEvent?: (oldValue: number, newValue: number) => void
}

ToggleMenu

封装组件

import { ToggleMenuOption } from "../Models/ToggleMenuOption";

@ComponentV2
export struct ToggleMenu {
  @Param Option: ToggleMenuOption = new ToggleMenuOption()

  aboutToAppear(): void {

  }

  build() {
    Column() {
      Toggle({ type: ToggleType.Button, isOn: $$this.Option.IsShowMenu }) {
        Row() {
          Text(this.Option.SelectValue)
            .fontWeight(FontWeight.Bold)
          Path()
            .width(20)
            .height(20)
            .commands(`M${vp2px(4)} ${vp2px(7)} L${vp2px(10)} ${vp2px(15)} L${vp2px(16)} ${vp2px(7)} Z`)
            .rotate(this.Option.IsShowMenu ? {
              centerX: "50%",
              centerY: "50%",
              angle: 180
            } : {
              angle: 0
            })
        }
        .justifyContent(FlexAlign.SpaceBetween)
        .padding({ left: 15, right: 15 })
        .height("100%")
        .width("100%")
      }
      .backgroundColor(this.Option.SelectedIndex == 0 ? this.Option.ToggleBackground :
      this.Option.ToggleUnFirstBackground)
      .selectedColor(this.Option.SelectedIndex == 0 ? this.Option.ToggleBackground : this.Option.ToggleUnFirstBackground)
      .width(this.Option.ToggleWidth)
      .height(this.Option.ToggleHeight)
      .bindMenu(this.Option.IsShowMenu, this.SortMenuBuilder(), {
        placement: Placement.Bottom,
        onDisappear: () => {
          if (this.Option.IsShowMenu) {
            this.Option.IsShowMenu = false;
          }
        }
      })
    }
    .height(this.Option.ToggleHeight)
  }

  @Builder
  SortMenuBuilder() {
    Column() {
      ForEach(this.Option.Items, (value: string, index: number) => {
        Row() {
          Text(value)
          Image($r('app.media.YesIcon'))
            .width(20)
            .height(20)
            .fillColor("#282828")
            .visibility(this.Option.SelectedIndex == index ? Visibility.Visible : Visibility.None)
        }
        .borderColor("#f7f7f7")
        .borderWidth({
          bottom: index == this.Option.Items.length - 1 ? 0 : 1
        })
        .justifyContent(FlexAlign.SpaceBetween)
        .alignItems(VerticalAlign.Center)
        .height(this.Option.MenuItemHeight)
        .width("100%")
        .onClick(() => {
          if (this.Option.SelectChangeEvent && this.Option.SelectedIndex != index) {
            this.Option.SelectChangeEvent(this.Option.SelectedIndex, index);
          }
          if (this.Option.SelectValueFormatAction) {
            this.Option.SelectValue = this.Option.SelectValueFormatAction(value);
          } else {
            this.Option.SelectValue = value;
          }
          this.Option.SelectedIndex = index;
          this.Option.IsShowMenu = false;
        })
      })
    }
    .padding(this.Option.MenuPadding)
    .width(this.Option.MenuWidth)
  }
}

总结

上述文章是对下拉框选择组件的一种封装和实现的方式,其中也遇到了一些问题,比如说:

  • 菜单组件的宽度设置成“100%”的时候,菜单会自动内缩一段距离,然后也没有办法进行调整,如果弹出菜单内容的宽度也设置成100%的话,会出现超出界面不显示的问题。(希望后面会解决这个问题)

希望这篇文章对大家可以有所帮助,或者有什么可以改进的地方,欢迎大家交流


更多关于HarmonyOS鸿蒙NEXT中自定义组件下拉选择框的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙NEXT中自定义组件下拉选择框的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙NEXT中,自定义下拉选择框可以通过Picker组件实现。首先,在resources目录下定义选择项,然后在布局文件中使用Picker组件,并通过setValueChangedListener监听选择变化。示例代码如下:

<Picker
    ohos:id="$+id:picker"
    ohos:width="match_parent"
    ohos:height="wrap_content"
    ohos:selected_text_size="30fp"
    ohos:normal_text_size="20fp"/>
Picker picker = (Picker) findComponentById(ResourceTable.Id_picker);
picker.setDisplayedData(new String[]{"选项1", "选项2", "选项3"});
picker.setValueChangedListener((picker, oldVal, newVal) -> {
    // 处理选择变化
});

通过这种方式,可以灵活定制下拉选择框的样式和功能。

回到顶部