HarmonyOS NEXT - picker 选择器( 包含 单列、多列、底部选择器)
demo 地址: https://github.com/iotjin/JhHarmonyDemo
组件对应代码实现地址
代码不定时更新,请前往github查看最新代码
在demo中这些组件和工具类都通过module实现了,具体可以参考HarmonyOS NEXT - 通过 module 模块化引用公共组件和utils
HarmonyOS NEXT - picker 选择器(包含 单列、多列、底部选择器)
- 效果图
- 调用方式
- JhPickerTool.ets 完整代码
官方dialog 比较反人类,调用比较麻烦,下面两种还略微好点
- 通过
promptAction.openCustomDialog
实现自定义弹窗。对应官方文档- 不依赖UI组件的全局自定义弹出框 (openCustomDialog)(推荐)对应官方文档
在三方库上找了一个弹框库,简单调整样式封装了一层
三方库地址:
@pura/harmony-dialog(V1.0.7)
需要先在项目中导入三方库
ohpm i @pura/harmony-dialog@1.0.7
OpenHarmony ohpm 环境配置等更多内容,请参考如何安装 OpenHarmony ohpm 包
!!!注意: 这个组件和JhDialog.ets 使用的同一配置,初始化使用Jhdialog 的初始化就可以了
下面是Jhdialog
使用的文章地址:
HarmonyOS NEXT - Dialog 和完全自定义弹框
效果图
![]() | ![]() | - |
---|---|---|
![]() | ![]() | ![]() |
调用方式
需要先全局初始化一次,否则弹框不显示
全局初始化可以放在入口的page处
1.0.8开始需要在在windowStage.loadContent 后执行初始化
以下是在第一个page的aboutToAppear方法初始化的
aboutToAppear() {
// 初始化Loading
let uiContext: UIContext = this.getUIContext();
JhDialog.initConfig(uiContext)
}
- BottomSheet-不带标题
JhPickerTool.showBottomSheet({
data: ['hello', 'world', '123'],
clickCallback: (index, text) => {
console.error('index', index)
console.error('text', text)
JhProgressHUD.showToast(text)
}
})
- BottomSheet-带标题
JhPickerTool.showBottomSheet({
title: '请选择',
data: ['hello', 'world', '123'],
clickCallback: (index, text) => {
console.error('index', index)
console.error('text', text)
JhProgressHUD.showToast(text)
}
})
- BottomSheet-红色按钮
JhPickerTool.showBottomSheet({
title: '请选择操作',
data: [{
value: "保存本地",
fontSize: 18
}, {
value: "删除",
fontColor: Color.Red,
fontSize: 18
}],
clickCallback: (index, text) => {
console.error('index', index)
console.error('text', text)
JhProgressHUD.showToast(text)
}
})
- JhPickerTool-单列(字符串数组)
const stringArr = ['11', '22', '33', '44']
JhPickerTool.showStringPicker({
data: stringArr,
title: '请选择类型',
selectIndex: 1,
onCancel: () => {
console.error(`点击了取消按钮`)
},
onConfirm: (selectValue, selectIndex: Number) => {
console.error(`点击了确认按钮`)
console.error('selectValue:', selectValue)
console.error('selectIndex:', selectIndex)
JhProgressHUD.showToast(selectValue.toString())
},
})
- JhPickerTool-单列(对象数组)
const dictArr: ItemType[] = [
{ 'label': '类型一', 'value': '1' },
{ 'label': '类型二', 'value': '2' },
{ 'label': '类型三', 'value': '3' },
]
JhPickerTool.showStringPicker({
data: dictArr,
title: '请选择',
labelKey: 'label',
selectIndex: 2,
onCancel: () => {
console.error(`点击了取消按钮`)
},
onConfirm: (selectValue, selectIndex: Number) => {
console.error(`点击了确认按钮`)
console.error('selectValue:', JSON.stringify(selectValue))
console.error('selectValue-label:', selectValue['label'])
console.error('selectIndex:', selectIndex)
JhProgressHUD.showToast(JSON.stringify(selectValue))
},
})
- JhPickerTool-两列(对象数组)
const dictArr2: ItemType[][] = [
[
{ 'label': '大类一', 'value': '1' },
{ 'label': '大类二', 'value': '2' },
{ 'label': '大类三', 'value': '3' },
],
[
{ 'label': '小类一', 'value': '11' },
{ 'label': '小类二', 'value': '22' },
],
]
JhPickerTool.showArrayPicker({
data: dictArr2,
title: '请选择',
selectIndex: [1, 1],
labelKey: 'label',
onCancel: () => {
console.error(`点击了取消按钮`)
},
onConfirm: (selectItemArr, selectIndexArr: Number[]) => {
console.error(`点击了确认按钮`)
console.error('selectItemArr:', JSON.stringify(selectItemArr))
console.error('selectIndexArr:', JSON.stringify(selectIndexArr))
JhProgressHUD.showToast(JSON.stringify(selectItemArr))
},
})
- JhPickerTool-三列(对象数组)
const dictArr3: ItemType[][] = [
[
{ 'label': '大类一', 'value': '1' },
{ 'label': '大类二', 'value': '2' },
{ 'label': '大类三', 'value': '3' },
],
[
{ 'label': '中类一', 'value': '1' },
{ 'label': '中型二', 'value': '2' },
{ 'label': '中型三', 'value': '3' },
{ 'label': '中型四', 'value': '4' },
],
[
{ 'label': '小类一', 'value': '1' },
{ 'label': '小类二', 'value': '2' },
],
]
JhPickerTool.showArrayPicker({
data: dictArr3,
title: '请选择',
selectIndex: [1, 2, 1],
labelKey: 'label',
onCancel: () => {
console.error(`点击了取消按钮`)
},
onConfirm: (selectItemArr, selectIndexArr: Number[]) => {
console.error(`点击了确认按钮`)
console.error('selectItemArr:', JSON.stringify(selectItemArr))
console.error('selectIndexArr:', JSON.stringify(selectIndexArr))
JhProgressHUD.showToast(JSON.stringify(selectItemArr))
},
})
JhPickerTool.ets 完整代码
/// JhPickerTool.ets
///
/// Created by iotjin on 2024/12/25.
/// description: @pura/harmony-dialog 封装。JhDialog、JhToast、JhPickerTool共用同一默认配置
import { SheetOptions, DialogAction, DialogHelper, OnActionCallBack } from "@pura/harmony-dialog"
import { KColors } from "../configs/Colors"
const _labelKey = 'label' // 对象数组的文字字段
const _titleText = '请选择'
const _cancelText = '取消'
const _confirmText = '确认'
const _kTitleFontSize = 18.0
const _kBtnFontSize = 17.0
const _textFontSize = 18.0
const _selectTextFontSize = 20.0
const _kHeaderRadius = 10.0
const _bgColor = KColors.kPickerBgColor
const _titleColor = KColors.kPickerTitleColor
const _btnColor = KColors.kPickerBtnColor
// const _headerColor = '#E6E6E6'
const _headerColor = KColors.kPickerHeaderColor
const _selectTextColor = KColors.kPickerTextColor
const _otherTextColor = KColors.kLightGreyTextColor
export interface JhPickerOptions {
data?: ESObject[] // 数据源
labelKey?: string, // 对象数组的文字字段, 默认_labelKey
selectIndex?: number | number[]
title?: ResourceStr
leftText?: ResourceStr
rightText?: ResourceStr
onCancel?: () => void
/// 选择回调
/// 单列选择器返回选中行对象和index
/// 多列选择器返回选中行对象数组和index数组
onConfirm?: (selectValue: string | string[], selectIndexArr: ESObject) => void
}
export interface JhBottomSheetOptions {
data?: Array<SheetOptions> | Array<ResourceStr>; // 数据源
title?: ResourceStr
/// 选择回调
/// index 从上往下 0,1,2
clickCallback?: (selectIndex: number, selectText: string) => void
}
export class JhPickerTool {
/**
* 底部弹框
* @param options
*/
public static showBottomSheet(options: JhBottomSheetOptions) {
if ((options.data ?? []).length == 0) {
return
}
DialogHelper.showBottomSheetDialog({
title: options.title ?? '',
titleFontColor: _titleColor,
cancelValue: _cancelText,
sheets: options.data ?? [],
cornerRadius: _kHeaderRadius,
backgroundColor: _bgColor,
autoCancel: true, // 点击遮障层时,是否关闭弹窗,true表示关闭弹窗。默认关闭
backCancel: false, // 点击返回键或手势返回时,是否关闭弹窗;实现onWillDismiss函数时,该参数不起作用。默认不关闭
actionCancel: true, // 点击操作按钮时,是否关闭弹窗。false表示不关闭弹窗。
onAction: (index) => {
const isStringArray = JhPickerTool.isOneStringArray(options.data)
const selectedText: string = isStringArray ? options.data![index] : options.data![index]['value']
options.clickCallback?.(index, selectedText)
}
})
}
/**
* 单列选择器
* 单列选择器返回选中行对象和index
* @param options
*/
public static showStringPicker(options: JhPickerOptions) {
const originalList: ESObject[] = options.data ?? []
if (originalList.length == 0 || !JhPickerTool.isOneDimensionalArray(originalList)) {
return
}
const isStringArray = JhPickerTool.isOneStringArray(originalList)
const labelKey = options.labelKey ?? _labelKey
const tempList: string[] = originalList.map((item: ESObject) => {
return (isStringArray ? item : item[labelKey]) as string
})
DialogHelper.showTextPickerDialog({
range: tempList,
value: tempList[options.selectIndex as number ?? 0],
title: options.title ?? _titleText,
titleFontColor: _titleColor,
titleBackground: _headerColor,
primaryButton: {
value: options.leftText ?? _cancelText,
fontColor: _btnColor,
},
secondaryButton: {
value: options.rightText ?? _confirmText,
fontColor: _btnColor,
},
//设置所有选项中最上和最下两个选项的文本颜色、字号、字体粗细。
disappearTextStyle: { color: _otherTextColor, font: { size: _textFontSize } },
//设置所有选项中除了最上、最下及选中项以外的文本颜色、字号、字体粗细。
textStyle: { color: _otherTextColor, font: { size: _textFontSize } },
//设置选中项的文本颜色、字号、字体粗细。
selectedTextStyle: { color: _selectTextColor, font: { size: _selectTextFontSize } },
cornerRadius: _kHeaderRadius,
backgroundColor: _bgColor,
canLoop: false,
autoCancel: true, // 点击遮障层时,是否关闭弹窗,true表示关闭弹窗。默认关闭
backCancel: false, // 点击返回键或手势返回时,是否关闭弹窗;实现onWillDismiss函数时,该参数不起作用。默认不关闭
actionCancel: true, // 点击操作按钮时,是否关闭弹窗。false表示不关闭弹窗。
onChange: (value: string | string[], index: number | number[]): void => {
// console.log(`value: ${value}`)
// console.log(`index: ${index}`)
},
onAction: (action: number, dialogId: string, value: string | string[]) => {
if (action == DialogAction.ONE) {
options.onCancel?.()
} else {
if (isStringArray) {
const foundIndex = tempList.findIndex((item) => item === value)
const selectIndex = foundIndex === -1 ? 0 : foundIndex
options.onConfirm?.(value, selectIndex)
} else {
const foundIndex = originalList.findIndex((item: ESObject) => item[labelKey] === value)
const selectIndex = foundIndex === -1 ? 0 : foundIndex
options.onConfirm?.(originalList[selectIndex], selectIndex)
}
}
}
})
}
/**
* 多列选择器
* 多列选择器返回选中行对象数组和index数组
* @param options
*/
public static showArrayPicker(options: JhPickerOptions) {
const originalList: ESObject[] = options.data ?? []
if (originalList.length == 0 || JhPickerTool.isOneDimensionalArray(originalList)) {
return
}
const isMultiStringArray = JhPickerTool.isMultiStringArray(originalList)
const labelKey = options.labelKey ?? _labelKey
let tempList: string[][] = originalList
if (!isMultiStringArray) {
const labelArr: string[][] = originalList.map((subArray: ESObject) => {
return subArray.map((item: ESObject) => item[labelKey] as string) as string[]
})
tempList = labelArr
}
let valueList: string[] = []
if (options.selectIndex && Array.isArray(options.selectIndex)) {
for (let i = 0; i < options.selectIndex!.length; i++) {
let columnData: ESObject = originalList[i]
const index = options.selectIndex![i]
if (index >= 0 && index < columnData.length) {
valueList.push(isMultiStringArray ? columnData[index] : columnData[index][labelKey])
} else {
valueList.push(isMultiStringArray ? columnData[0] : columnData[0][labelKey])
}
}
}
DialogHelper.showTextPickerDialog({
range: tempList,
value: valueList,
title: options.title ?? _titleText,
titleFontColor: _titleColor,
titleBackground: _headerColor,
primaryButton: {
value: options.leftText ?? _cancelText,
fontColor: _btnColor,
},
secondaryButton: {
value: options.rightText ?? _confirmText,
fontColor: _btnColor,
},
//设置所有选项中最上和最下两个选项的文本颜色、字号、字体粗细。
disappearTextStyle: { color: _otherTextColor, font: { size: _textFontSize } },
//设置所有选项中除了最上、最下及选中项以外的文本颜色、字号、字体粗细。
textStyle: { color: _otherTextColor, font: { size: _textFontSize } },
//设置选中项的文本颜色、字号、字体粗细。
selectedTextStyle: { color: _selectTextColor, font: { size: _selectTextFontSize } },
cornerRadius: _kHeaderRadius,
backgroundColor: _bgColor,
canLoop: false,
autoCancel: true, // 点击遮障层时,是否关闭弹窗,true表示关闭弹窗。默认关闭
backCancel: false, // 点击返回键或手势返回时,是否关闭弹窗;实现onWillDismiss函数时,该参数不起作用。默认不关闭
actionCancel: true, // 点击操作按钮时,是否关闭弹窗。false表示不关闭弹窗。
onChange: (value: string | string[], index: number | number[]): void => {
// console.log(`value: ${value}`)
// console.log(`index: ${index}`)
},
onAction: (action: number, dialogId: string, values: string | string[]) => {
if (action == DialogAction.ONE) {
options.onCancel?.()
} else {
if (Array.isArray(values)) {
let selectItemArr: ESObject = []
let selectIndexArr: number[] = []
for (let i = 0; i < values.length; i++) {
let columnData: ESObject = originalList[i]
const value = values[i]
const foundIndex: number = columnData.findIndex((item: ESObject) => (isMultiStringArray ? item : item[labelKey]) === value)
const selectIndex = foundIndex === -1 ? 0 : foundIndex
selectIndexArr.push(selectIndex)
selectItemArr.push(columnData[selectIndex])
}
options.onConfirm?.(selectItemArr, selectIndexArr)
}
}
}
})
}
/**
* 是否是一维数组
* @param arr
* @returns
*/
public static isOneDimensionalArray(arr: ESObject[]) {
// 检查是否为数组,并且数组内元素不是数组, 使用 some 方法检查是否有任何元素是数组,如果存在,则说明是非一维数组
return Array.isArray(arr) && !arr.some((item: ESObject) => Array.isArray(item))
}
/**
* 是否是一维字符串数组
* @param arr
* @returns
*/
public static isOneStringArray(arr: ESObject[]) {
return Array.isArray(arr) && arr.every((item: ESObject) => typeof item === 'string')
}
/**
* 是否是多维字符串数组
* @param arr
* @returns
*/
public static isMultiStringArray(arr: ESObject[]) {
// 如果不是数组,直接返回 false
if (!Array.isArray(arr)) {
return false
}
// 检查数组中的每个元素是否是字符串数组
return arr.every((item: ESObject): boolean => JhPickerTool.isOneStringArray(item))
}
}