ArkUI 学习之自定义内容(contentModifier)
一、概述
ContentModifier 是一个在鸿蒙(HarmonyOS)开发中使用的接口,允许开发者自定义组件的内容区。通过实现这个接口,开发者可以定义如何修改或自定义组件的显示和行为。
contentModifier(modifier:ContentModifier<T>)
这个属性不是所有的组件都有,仅仅下面这些组件才有
按钮组件:Button
多选框组件:CheckBox
数据面板组件:DataPanel
文本钟组件:TextClock
开关组件:Toggle
数据量规图表组件:Gauge
加载动效的组件:LoadingProgress
单选框组件:Radio
进度条组件:Progress
评分组件:Rating
滑动条组件:Slider
参数:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
modifier | ContentModifier<T> | 是 |
在当前组件上,定制内容区的方法。 |
🔈:modifier:内容修改器,开发者需要自定义class实现ContentModifier接口。
class MyContentModifier implements ContentModifier<ButtonConfiguration> { /** ! 成员变量 */ /** ! 构造函数 */ constructor() {} /** ! 自定义内容区域 */ applyContent(): WrappedBuilder<[ButtonConfiguration]> { return wrapBuilder(buildCheckbox) } }/** 自定义Builder */ @Builder function buildCheckbox(config: ButtonConfiguration) { }
二、ContentModifier<T>
applyContent
applyContent() : WrappedBuilder<[T]>
定制内容区的Builder。
参数:
参数 | 描述 |
---|---|
T | 组件的属性类,用来区别不同组件自定义内容区后所需要的不同信息,比如Button组件的ButtonConfiguration,Checkbox组件的CheckBoxConfiguration等。 |
ButtonConfiguration、
CheckBoxConfiguration、
DataPanelConfiguration、
TextClockConfiguration、
ToggleConfiguration、
GaugeConfiguration、
LoadingProgressConfiguration、
RadioConfiguration、
ProgressConfiguration、
RatingConfiguration、
SliderConfiguration
三、CommonConfiguration<T>
开发者需要自定义class实现ContentModifier接口。
参数名 | 类型 | 说明 |
---|---|---|
enabled | boolean | 如果该值为true,则contentModifier可用,并且可以响应triggerChange等操作,如果设置为false,则不会响应triggerChange等操作。 |
contentModifier | ContentModifier<T> | 用于将用户需要的组件信息发送到自定义内容区。 |
四、示例
🍀 1. ButtonConfiguration
🦋 接口:
declare interface ButtonConfiguration extends CommonConfiguration<ButtonConfiguration> { //Button的文本标签。 label: string; //指示是否按下Button。 //说明:此属性指示的是原本Button是否被按压,而非build出来的新组件。若新build出来 //的组件超过原本组件的大小,那么超出部分按压不触发。 pressed: boolean; //使用builder新构建出来组件的点击事件。 triggerClick: ButtonTriggerClickCallback; }
🌰 例子:
class MyButtonContentModifier implements ContentModifier<ButtonConfiguration> { /** ! 实例变量 */ pressColor: ResourceColor /** ! 构造器 */ constructor(pressColor: ResourceColor) { this.pressColor = pressColor } /** ! 内容函数 */ applyContent(): WrappedBuilder<[ButtonConfiguration]> { return wrapBuilder(buttonContentModifier) } } @Builder function buttonContentModifier(config: ButtonConfiguration) { Column({ space: 10 }) { if (config.pressed) { Text('这是一个自定义文本按钮').fontColor(Color.Green) Image($r('app.media.icon_one')) } else { Image($r('app.media.icon_two')) Text('这是一个自定义文本按钮').fontColor((config.contentModifier as MyButtonContentModifier).pressColor) } } } @Entry @Component struct Index { /** MARK: 成员变量 */ /** MARK: 构建函数 */ build() { Row() { Column({ space: 50 }) { Button('正常按钮') .width('100%') .height(50) .backgroundColor(Color.Blue) Button('自定义内容按钮') .width('100%') .height(50) .contentModifier(new MyButtonContentModifier(Color.Red)) .backgroundColor(Color.Gray) }.width('100%').padding({ left: 20, right: 20 }) }.height('100%') } }
🍀 2. CheckBoxConfiguration
🦋 接口:
declare interface CheckBoxConfiguration extends CommonConfiguration<CheckBoxConfiguration> { //当前多选框名称。 name: string; //指示多选框是否被选中。 //如果select属性没有设置默认值是false。 //如果设置select属性,此值与设置select属性的值相同。 selected: boolean; //触发多选框选中状态变化。 triggerChange: Callback<boolean>; }
🌰 例子:
class MyCheckBoxContentModifier implements ContentModifier<CheckBoxConfiguration> { /** ! 成员变量 */ /** ! 构造函数 */ constructor() {} /** ! 自定义内容 */ applyContent(): WrappedBuilder<[CheckBoxConfiguration]> { return wrapBuilder(checkBoxContentModifier) } } @Builder function checkBoxContentModifier(config: CheckBoxConfiguration) { Text(config.name + (config.selected ? "( 选中 )" : "( 非选中 )")) .onClick(() => { config.triggerChange(!config.selected) /** ⚠️注意:通过triggerChange改变select状态 */ }) } @Entry @Component struct Index { /** MARK: 成员变量 */ @State checkBoxEnabled: boolean = true; /** MARK: 构建函数 */ build() { Row() { Column() { Column({ space: 100 }) { /** 勾选框 */ Row({ space: 10 }) { Text('正常:').fontWeight(500) Checkbox({ name: '复选框状态', group: 'checkboxGroup' }) .enabled(this.checkBoxEnabled) .onChange((value: boolean) => { console.info('Checkbox change is' + value) }) } Row({ space: 10 }) { Text('自定义内容:').fontWeight(500) Checkbox({ name: '复选框状态', group: 'checkboxGroup' }) .enabled(this.checkBoxEnabled) .contentModifier(new MyCheckBoxContentModifier()) .onChange((value: boolean) => { console.info('Checkbox change is' + value) }) } /** 开关 */ Row() { Toggle({ type: ToggleType.Switch, isOn: true }) .onChange((value: boolean) => { if (value) { this.checkBoxEnabled = true } else { this.checkBoxEnabled = false } }) } } }.width('100%').padding({ left: 20, right: 20 }) }.height('100%') } }
🍀 3. DataPanelConfiguration
🦋 接口:
declare interface DataPanelConfiguration extends CommonConfiguration<DataPanelConfiguration> { //当前DataPanel的数据值,最大长度为9。 values: number[]; //DataPanel显示的最大值。默认值:100。 maxValue: number; }
🌰 例子:
// xxx.ets class DataPanelBuilder implements ContentModifier<DataPanelConfiguration> { /** ! 构造器 */ constructor() {} /** ! 自定义内容 */ applyContent(): WrappedBuilder<[DataPanelConfiguration]> { return wrapBuilder(buildDataPanel) } } @Builder function buildDataPanel(config: DataPanelConfiguration) { Column() { Column() { ForEach(config.values, (item: number, index: number) => { ChildItem({ item: item, index: index, max: config.maxValue }) }, (item: string) => item) }.padding(10) Column() { Line().width("100%").backgroundColor("#ff373737").margin({ bottom: 5 }) }.padding({ left: 20, right: 20 }) Row() { Text('Length=' + config.values.length + ' ').margin({ left: 10 }).align(Alignment.Start) Text('Max=' + config.maxValue).margin({ left: 10 }).align(Alignment.Start) } } } @Entry @Component struct Index { /** MARK: 构造函数 */ build() { Column() { Text("Data panel").margin({ top: 12 }); Row() { DataPanel({ values: [12.3, 21.1, 13.4, 35.2, 26.0, 32.0], max: 140, type: DataPanelType.Circle }) .width(400) .height(260) .constraintSize({ maxWidth: "100%" }) .padding({ top: 10 }) .contentModifier(new DataPanelBuilder()) }.margin(15).backgroundColor("#fff5f5f5") } } } @Component struct ChildItem { /** MARK: 成员变量 */ @Prop item: number; @Prop index: number; @Prop max: number; public color1: string = "#65ff00dd" public color2: string = "#6500ff99" public color3: string = "#65ffe600" public color4: string = "#6595ff00" public color5: string = "#65000dff" public color6: string = "#650099ff" public colorArray: Array<string> = [this.color1, this.color2, this.color3, this.color4, this.color5, this.color6] /** MARK: 构建函数 */ build() { RelativeContainer() { Row() { Rect() .height(25) .width(this.item * 600 / this.max) .foregroundColor(this.colorArray[this.index]) .radius(5) .align(Alignment.Start) Text(" " + this.item) .fontSize(17) } }.height(28) } }
🍀 4. TextClockConfiguration
🦋 接口:
declare interface TextClockConfiguration extends CommonConfiguration<TextClockConfiguration> { //当前文本时钟时区偏移量。 timeZoneOffset: number; //指示文本时钟是否启动。默认值:true。 started: boolean; //当前文本时钟时区的UTC秒数。 timeValue: number; }
🌰 例子:该示例实现了自定义文本时钟样式的功能,自定义样式实现了一个时间选择器组件:通过文本时钟的时区偏移量与UTC秒数,来动态改变时间选择器的选中值,实现时钟效果。同时,根据文本时钟的启动状态,实现文本选择器的12小时制与24小时制的切换。
class MyTextClockStyle implements ContentModifier<TextClockConfiguration> { /** MARK: 成员变量 */ currentTimeZoneOffset: number = new Date().getTimezoneOffset() / 60 title: string = '' /** MARK: 构造器 */ constructor(title: string) { this.title = title } /** MARK: 自定义内容 */ applyContent(): WrappedBuilder<[TextClockConfiguration]> { return wrapBuilder(buildTextClock) } } @Builder function buildTextClock(config: TextClockConfiguration) { Row() { Column() { Text((config.contentModifier as MyTextClockStyle).title) .fontSize(20) .margin(20) TimePicker({ selected: (new Date(config.timeValue * 1000 + ((config.contentModifier as MyTextClockStyle).currentTimeZoneOffset - config.timeZoneOffset) * 60 * 60 * 1000)), format: TimePickerFormat.HOUR_MINUTE_SECOND }).useMilitaryTime(!config.started) } } } @Entry @Component struct TextClockExample { /** MARK: 成员变量 */ @State accumulateTime1: number = 0 @State timeZoneOffset: number = -8 private controller1: TextClockController = new TextClockController() private controller2: TextClockController = new TextClockController() /** MARK: 构建函数 */ build() { Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Text('Current milliseconds is ' + this.accumulateTime1) .fontSize(20) .margin({ top: 20 }) TextClock({ timeZoneOffset: this.timeZoneOffset, controller: this.controller1 }) .format('aa hh:mm:ss') .onDateChange(this.onDateChange) .margin(20) .fontSize(30) TextClock({ timeZoneOffset: this.timeZoneOffset, controller: this.controller2 }) .format('aa hh:mm:ss') .fontSize(30) .contentModifier(new MyTextClockStyle('ContentModifier:')) Button("start TextClock") .margin({ top: 20, bottom: 10 }) .onClick(this.onStartTextClock) Button("stop TextClock") .margin({ bottom: 30 }) .onClick(this.onEndTextClock) } .width('100%') .height('100%') } /** MARK: 成员函数 */ // 启动文本时钟 private onStartTextClock = () => { this.controller1.start() this.controller2.start() } // 停止文本时钟 private onEndTextClock = () => { this.controller1.stop() this.controller2.stop() } // 数据改变事件 private onDateChange = (value: number) => { this.accumulateTime1 = value } }
🍀 5. ToggleConfiguration
🦋 接口:
declare interface ToggleConfiguration extends CommonConfiguration<ToggleConfiguration> { //开关是否打开。默认值:false isOn: boolean; //是否可以切换状态。 enabled: boolean; //触发switch选中状态变化。 triggerChange: Callback<boolean>; }
🌰 例子:该示例实现了自定义Toggle样式的功能。自定义样式实现了通过按钮切换圆形颜色的功能:点击蓝圆按钮,圆形背景变蓝色,点击黄圆按钮,圆形背景变黄色。
// xxx.ets class MySwitchStyle implements ContentModifier<ToggleConfiguration> { /** ! 成员变量 */ selectedColor: Color = Color.White lamp: string = 'string'; /** ! 构造器 */ constructor(selectedColor: Color, lamp: string) { this.selectedColor = selectedColor this.lamp = lamp; } /** ! 自定义内容 */ applyContent(): WrappedBuilder<[ToggleConfiguration]> { return wrapBuilder(buildSwitch) } } @Builder function buildSwitch(config: ToggleConfiguration) { Column({ space: 50 }) { Circle({ width: 150, height: 150 }) .fill(config.isOn ? (config.contentModifier as MySwitchStyle).selectedColor : Color.Blue) Row() { Button('蓝' + JSON.stringify((config.contentModifier as MySwitchStyle).lamp)) .onClick(() => { config.triggerChange(false); }) Button('黄' + JSON.stringify((config.contentModifier as MySwitchStyle).lamp)) .onClick(() => { config.triggerChange(true); }) } } } @Entry @Component struct Index { build() { Column({ space: 50 }) { Toggle({ type: ToggleType.Switch }) .enabled(true) .contentModifier(new MySwitchStyle(Color.Yellow, '灯')) .onChange((isOn: boolean) => { console.info('Switch Log:' + isOn) }) }.height('100%').width('100%') } }
🍀 6. GaugeConfiguration
🦋 接口:
declare interface GaugeConfiguration extends CommonConfiguration<GaugeConfiguration> { //当前数据值。 value: number; //当前数据段最小值。 min: number; //当前数据段最大值 max: number; }
🌰 例子:
该示例通过contentModifier接口,实现了定制量规图内容区的功能。
class MyGaugeStyle implements ContentModifier<GaugeConfiguration> { /** MARK: 成员变量 */ value: number = 0 min: number = 0 max: number = 0 /** MARK: 构造函数 */ constructor(value: number, min: number, max: number) { this.value = value this.min = min this.max = max } /** MARK: 自定义内容 */ applyContent(): WrappedBuilder<[GaugeConfiguration]> { return wrapBuilder(buildGauge) } } @Builder function buildGauge(config: GaugeConfiguration) { Column({ space: 30 }) { Row() { Text('【ContentModifier】 value:' + JSON.stringify((config.contentModifier as MyGaugeStyle).value) + ' min:' + JSON.stringify((config.contentModifier as MyGaugeStyle).min) + ' max:' + JSON.stringify((config.contentModifier as MyGaugeStyle).max)) .fontSize(12) } Text('【Config】value:' + config.value + ' min:' + config.min + ' max:' + config.max).fontSize(12) Gauge({ value: config.value, min: config.min, max: config.max }).width("50%") } .width("100%") .padding(20) .margin({ top: 5 }) .alignItems(HorizontalAlign.Center) } @Entry @Component struct refreshExample { /** MARK: 成员变量 */ @State gaugeValue: number = 20 @State gaugeMin: number = 0 @State gaugeMax: number = 100 /** MARK: 构建函数 */ build() { Column({ space: 20 }) { Gauge({ value: this.gaugeValue, min: this.gaugeMin, max: this.gaugeMax }).contentModifier(new MyGaugeStyle(30, 10, 100)) Column({ space: 20 }) { Row({ space: 20 }) { Button('增加').onClick(() => { if (this.gaugeValue < this.gaugeMax) { this.gaugeValue += 1 } }) Button('减少').onClick(() => { if (this.gaugeValue > this.gaugeMin) { this.gaugeValue -= 1 } }) } }.width('100%') }.width('100%').margin({ top: 5 }) } }
🍀 7. LoadingProgressConfiguration
🦋 接口:
declare interface LoadingProgressConfiguration extends CommonConfiguration<LoadingProgressConfiguration> { // LoadingProgress动画是否显示。默认值:true enableLoading: boolean; }
🌰 例子:
该示例通过contentModifier接口,实现了定制内容区的功能,并通过enableLoading接口实现了通过按钮切换是否显示LoadingProgress的效果。
// xxx.ets import { promptAction } from '@kit.ArkUI' class MyLoadingProgressStyle implements ContentModifier<LoadingProgressConfiguration> { /** MARK: 成员变量 */ enableLoading: boolean = false /** MARK: 构造器 */ constructor(enableLoading: boolean) { this.enableLoading = enableLoading } /** MARK: 自定义内容 */ applyContent(): WrappedBuilder<[LoadingProgressConfiguration]> { return wrapBuilder(buildLoadingProgress) } } let arr1: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"] let arr2: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] @Builder function buildLoadingProgress(config: LoadingProgressConfiguration) { Column({ space: 8 }) { Row() { Column() { Circle({ width: ((config.contentModifier as MyLoadingProgressStyle).enableLoading) ? 100 : 80, height: ((config.contentModifier as MyLoadingProgressStyle).enableLoading) ? 100 : 80 }) .fill(((config.contentModifier as MyLoadingProgressStyle).enableLoading) ? Color.Grey : 0x2577e3) }.width('50%') Column() { Button('' + ((config.contentModifier as MyLoadingProgressStyle).enableLoading)) .onClick((event: ClickEvent) => { promptAction.showToast({ message: ((config.contentModifier as MyLoadingProgressStyle).enableLoading) + '' }) }) .fontColor(Color.White) .backgroundColor(((config.contentModifier as MyLoadingProgressStyle).enableLoading) ? Color.Grey : 0x2577e3) }.width('50%') } Row() { Column() { Gauge({ value: (config.contentModifier as MyLoadingProgressStyle).enableLoading ? 50 : 30, min: 11, max: 100 }) { Column() { Text('60') .maxFontSize("180sp") .minFontSize("160.0vp") .fontWeight(FontWeight.Medium) .fontColor("#ff182431") .width('40%') .height('30%') .textAlign(TextAlign.Center) .margin({ top: '22.2%' }) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) }.width('100%').height('100%') } .colors(((config.contentModifier as MyLoadingProgressStyle).enableLoading) ? Color.Grey : 0x2577e3) .width(200) .strokeWidth(18) .padding(5) .trackShadow({ radius: 7, offsetX: 7, offsetY: 7 }) .height(200) }.width('100%') } Column() { List({ space: 20, initialIndex: 0 }) { ForEach(arr2, (item: string) => { ListItem() { Text((config.contentModifier as MyLoadingProgressStyle).enableLoading ? '' + item : Number(item) * 2 + '') .width('100%') .height('100%') .fontColor((config.contentModifier as MyLoadingProgressStyle).enableLoading ? Color.White : Color.Orange) .fontSize((config.contentModifier as MyLoadingProgressStyle).enableLoading ? 16 : 20) .textAlign(TextAlign.Center) .backgroundColor((config.contentModifier as MyLoadingProgressStyle).enableLoading ? Color.Grey : 0x2577e3) } .height(110) .border({ width: 2, color: Color.White }) }, (item: string) => item) } .height(200) .width('100%') .friction(0.6) .lanes({ minLength: (config.contentModifier as MyLoadingProgressStyle).enableLoading ? 40 : 80, maxLength: (config.contentModifier as MyLoadingProgressStyle).enableLoading ? 40 : 80 }) .scrollBar(BarState.Off) } }.width("100%").padding(10) } @Entry @Component struct LoadingProgressDemoExample { /** MARK: 成员变量 */ @State loadingProgressList: (boolean | undefined | null)[] = [undefined, true, null, false] @State widthList: (number | string)[] = ['110%', 220, '40%', 80] @State loadingProgressIndex: number = 0 @State clickFlag: number = 0 private scroller: Scroller = new Scroller() /** MARK: 构建函数 */ build() { Column() { /** 滚动视图 */ Scroll(this.scroller) { Column({ space: 5 }) { Column() { LoadingProgress() .color('#106836') .size({ width: '100%' }) .contentModifier(new MyLoadingProgressStyle(this.loadingProgressList[this.loadingProgressIndex])) }.width('100%').backgroundColor(0xdcdcdc) }.width('100%').margin({ top: 5 }) }.height('85%') /** 按钮 */ Button('点击切换config.enableloading').onClick(this.onClickEvent).margin(20) } } /** MARK: 成员函数 */ private onClickEvent = () => { this.clickFlag++ this.loadingProgressIndex = (this.loadingProgressIndex + 1) % this.loadingProgressList.length console.log('enableLoading:' + this.loadingProgressList[this.loadingProgressIndex]) } }
🍀 8. RadioConfiguration
🦋 接口:
declare interface RadioConfiguration extends CommonConfiguration<RadioConfiguration> { // 当前单选框的值。 value: string; // 设置单选框的选中状态。 默认值:false checked: boolean; // 触发单选框选中状态变化。 triggerChange: Callback<boolean>; }
🌰 例子:
该示例通过contentModifier实现自定义单选框样式。
class MyRadioStyle implements ContentModifier<RadioConfiguration> { /** MARK: 成员变量 */ type: number = 0 selectedColor: ResourceColor = Color.Black /** MARK: 构造器 */ constructor(numberType: number, colorType: ResourceColor) { this.type = numberType this.selectedColor = colorType } /** MARK: 自定义内容 */ applyContent(): WrappedBuilder<[RadioConfiguration]> { return wrapBuilder(buildRadio) } } /** 构建函数 */ @Builder function buildRadio(config: RadioConfiguration) { Row({ space: 30 }) { Circle({ width: 50, height: 50 }) .stroke(Color.Black) .fill(config.checked ? (config.contentModifier as MyRadioStyle).selectedColor : Color.White) Button(config.checked ? "off" : "on") .width(100) .type(config.checked ? (config.contentModifier as MyRadioStyle).type : ButtonType.Normal) .backgroundColor('#2787D9') .onClick(() => { if (config.checked) { config.triggerChange(false) } else { config.triggerChange(true) } }) } } @Entry @Component struct refreshExample { build() { Column({ space: 50 }) { /** 样式一: */ Row() { Radio({ value: 'Radio1', group: 'radioGroup' }) .contentModifier(new MyRadioStyle(1, '#004AAF')) .checked(false) .width(300) .height(100) } /** 样式二: */ Row() { Radio({ value: 'Radio2', group: 'radioGroup' }) .checked(true) .width(300) .height(60) .contentModifier(new MyRadioStyle(2, '#004AAF')) } } } }
🍀 9. ProgressConfiguration
🦋 接口:
declare interface ProgressConfiguration extends CommonConfiguration<ProgressConfiguration> { //当前进度值。 value: number; // 进度总长。 total: number; }
🌰 例子:
该示例通过contentModifier接口,实现了自定义进度条的功能,自定义实现星形,其中总进度为3,且当前值可通过按钮进行增减,达到的进度被填充自定义颜色。
// xxx.ets class MyProgressModifier implements ContentModifier<ProgressConfiguration> { /** MARK: 成员变量 */ color: Color = Color.White /** MARK: 构造器 */ constructor(color: Color) { this.color = color } /** MARK: 自定义内容 */ applyContent(): WrappedBuilder<[ProgressConfiguration]> { return wrapBuilder(myProgress) } } @Builder function myProgress(config: ProgressConfiguration) { Column({ space: 30 }) { Text("当前进度:" + config.value + "/" + config.total).fontSize(20) Row() { Flex({ justifyContent: FlexAlign.SpaceBetween }) { Path() .width('30%') .height('30%') .commands('M108 0 L141 70 L218 78.3 L162 131 L175 205 L108 170 L41.2 205 L55 131 L1 78 L75 68 L108 0 Z') .fill(config.enabled && config.value >= 1 ? (config.contentModifier as MyProgressModifier).color : Color.White) .stroke(Color.Black) .strokeWidth(3) Path() .width('30%') .height('30%') .commands('M108 0 L141 70 L218 78.3 L162 131 L175 205 L108 170 L41.2 205 L55 131 L1 78 L75 68 L108 0 Z') .fill(config.enabled && config.value >= 2 ? (config.contentModifier as MyProgressModifier).color : Color.White) .stroke(Color.Black) .strokeWidth(3) Path() .width('30%') .height('30%') .commands('M108 0 L141 70 L218 78.3 L162 131 L175 205 L108 170 L41.2 205 L55 131 L1 78 L75 68 L108 0 Z') .fill(config.enabled && config.value >= 3 ? (config.contentModifier as MyProgressModifier).color : Color.White) .stroke(Color.Black) .strokeWidth(3) }.width('100%') } }.margin({ bottom: 100 }) } @Entry @Component struct Index { /** MARK: 成员变量 */ @State currentValue: number = 0 @State myModifier: (MyProgressModifier | undefined) = this.modifier private modifier = new MyProgressModifier(Color.Red) /** MARK: 构造函数 */ build() { Column() { /** 进度条 */ Progress({ value: this.currentValue, total: 3, type: ProgressType.Ring }).contentModifier(this.modifier) /** 操作按钮 */ Column() { Button('Progress++').onClick(() => { if (this.currentValue < 3) { this.currentValue += 1 } }).width('30%') Button('addProgress--').onClick(() => { if (this.currentValue > 0) { this.currentValue -= 1 } }).width('30%') } }.width('100%').height('100%') } }
🍀 10. RatingConfiguration
🦋 接口:
declare interface RatingConfiguration extends CommonConfiguration<RatingConfiguration> { // 评分条当前评分数。默认值:0 rating: number; //评分条是否作为一个指示器。默认值:false indicator: boolean; //评分条的星级总数。默认值:5 stars: number; //评分条的评分步长。默认值:0.5 stepSize: number; //触发评分数量变化。 triggerChange: Callback<number>; }
🌰 例子:
该示例实现了自定义评分条的功能,每个圆圈表示0.5分。ratingIndicator为true时表示评分条作为一个指示器不可改变评分;
为false时可以进行评分。ratingStars可改变评分总数。ratingStepsize可改变评分步长。
// xxx.ets class MyRatingStyle implements ContentModifier<RatingConfiguration> { /** MARK: 成员变量 */ name: string = "" style: number = 0 /** MARK: 构造器 */ constructor(value1: string, value2: number) { this.name = value1 this.style = value2 } /** MARK: 自定义内容 */ applyContent(): WrappedBuilder<[RatingConfiguration]> { return wrapBuilder(buildRating) } } /** 构造函数 */ @Builder function buildRating(config: RatingConfiguration) { Column() { Row() { Circle({ width: 25, height: 25 }) .fill(config.rating >= 0.4 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { if (config.stepSize = 0.5) { config.triggerChange(0.5); return } if (config.stepSize = 1) { config.triggerChange(1); return } } }).visibility(config.stars >= 1 ? Visibility.Visible : Visibility.Hidden) Circle({ width: 25, height: 25 }) .fill(config.rating >= 0.9 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { config.triggerChange(1); } }).visibility(config.stars >= 1 ? Visibility.Visible : Visibility.Hidden) Circle({ width: 25, height: 25 }) .fill(config.rating >= 1.4 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { if (config.stepSize = 0.5) { config.triggerChange(1.5); return } if (config.stepSize = 1) { config.triggerChange(2); return } } }).visibility(config.stars >= 2 ? Visibility.Visible : Visibility.Hidden).margin({ left: 10 }) Circle({ width: 25, height: 25 }) .fill(config.rating >= 1.9 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { config.triggerChange(2); } }).visibility(config.stars >= 2 ? Visibility.Visible : Visibility.Hidden) Circle({ width: 25, height: 25 }) .fill(config.rating >= 2.4 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { if (config.stepSize = 0.5) { config.triggerChange(2.5); return } if (config.stepSize = 1) { config.triggerChange(3); return } } }).visibility(config.stars >= 3 ? Visibility.Visible : Visibility.Hidden).margin({ left: 10 }) Circle({ width: 25, height: 25 }) .fill(config.rating >= 2.9 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { config.triggerChange(3); } }).visibility(config.stars >= 3 ? Visibility.Visible : Visibility.Hidden) Circle({ width: 25, height: 25 }) .fill(config.rating >= 3.4 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { if (config.stepSize = 0.5) { config.triggerChange(3.5); return } if (config.stepSize = 1) { config.triggerChange(4); return } } }).visibility(config.stars >= 4 ? Visibility.Visible : Visibility.Hidden).margin({ left: 10 }) Circle({ width: 25, height: 25 }) .fill(config.rating >= 3.9 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { config.triggerChange(4); } }).visibility(config.stars >= 4 ? Visibility.Visible : Visibility.Hidden) Circle({ width: 25, height: 25 }) .fill(config.rating >= 4.4 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { if (config.stepSize = 0.5) { config.triggerChange(4.5); return } if (config.stepSize = 1) { config.triggerChange(5); return } } }).visibility(config.stars >= 5 ? Visibility.Visible : Visibility.Hidden).margin({ left: 10 }) Circle({ width: 25, height: 25 }) .fill(config.rating >= 4.9 ? Color.Black : Color.Red) .onClick((event: ClickEvent) => { if (!config.indicator) { config.triggerChange(5); } }).visibility(config.stars >= 5 ? Visibility.Visible : Visibility.Hidden) } Text("分值:" + config.rating) } } @Entry @Component struct ratingExample { /** MARK: 成员变量 */ @State rating: number = 0; @State ratingIndicator: boolean = true; @State ratingStars: number = 0; @State ratingStepsize: number = 0.5; @State ratingEnabled: boolean = true; /** MARK: 构造函数 */ build() { Row() { Column() { Rating({ rating: 0, indicator: this.ratingIndicator }) .stepSize(this.ratingStepsize) .stars(this.ratingStars) .backgroundColor(Color.Transparent) .width('100%') .height(50) .onChange((value: number) => { console.info('Rating change is' + value); this.rating = value }) .contentModifier(new MyRatingStyle("hello", 3)) Column({ space: 20 }) { /** 按钮一 */ Button(this.ratingIndicator ? "ratingIndicator : true" : "ratingIndicator : false") .onClick((event) => { if (this.ratingIndicator) { this.ratingIndicator = false } else { this.ratingIndicator = true } }).margin({ top: 5 }) /** 按钮二 */ Button(this.ratingStars < 5 ? "ratingStars + 1, ratingStars =" + this.ratingStars : "ratingStars最大值为5") .onClick((event) => { if (this.ratingStars < 5) { this.ratingStars += 1 } }) /** 按钮三 */ Button(this.ratingStars > 0 ? "ratingStars - 1, ratingStars =" + this.ratingStars : "ratingStars小于等于0时默认等于5") .onClick((event) => { if (this.ratingStars > 0) { this.ratingStars -= 1 } }) /** 按钮四 */ Button(this.ratingStepsize == 0.5 ? "ratingStepsize : 0.5" : "ratingStepsize : 1") .onClick((event) => { if (this.ratingStepsize == 0.5) { this.ratingStepsize = 1 } else { this.ratingStepsize = 0.5 } }) } } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } .height('100%') } }
🍀 11. SliderConfiguration
🦋 接口:
declare interface SliderConfiguration extends CommonConfiguration<SliderConfiguration> { //当前进度值 value: number; //最小值。 min: number; //最大值。 max: number; //Slider滑动步长. step: number; //触发Slider变化。 triggerChange: SliderTriggerChangeCallback; }
🌰 例子:
/** ! 构造函数 */ @Builder function buildSlider(config: SliderConfiguration) { Row() { Column({ space: 30 }) { Progress({ value: config.value, total: config.max, type: ProgressType.Ring }) .margin({ top: 20 }) Button('增加') .onClick(() => { config.value = config.value + config.step config.triggerChange(config.value, SliderChangeMode.Click) }) .width(100) .height(25) .fontSize(10) .enabled(config.value < config.max) Button('减少') .onClick(() => { config.value = config.value - config.step config.triggerChange(config.value, SliderChangeMode.Click) }) .width(100) .height(25) .fontSize(10) .enabled(config.value > config.min) Slider({ value: config.value, min: config.min, max: config.max, step: config.step, }) .width(config.max) .visibility((config.contentModifier as MySliderStyle).showSlider ? Visibility.Visible : Visibility.Hidden) .showSteps(true) .onChange((value: number, mode: SliderChangeMode) => { config.triggerChange(value, mode) }) Text('当前状态:' + ((config.contentModifier as MySliderStyle).sliderChangeMode == 0 ? "Begin" : ((config.contentModifier as MySliderStyle).sliderChangeMode == 1 ? "Moving" : ((config.contentModifier as MySliderStyle).sliderChangeMode == 2 ? "End" : ((config.contentModifier as MySliderStyle).sliderChangeMode == 3 ? "Click" : "无"))))) .fontSize(10) Text('进度值:' + config.value) .fontSize(10) Text('最小值:' + config.min) .fontSize(10) Text('最大值:' + config.max) .fontSize(10) Text('步长:' + config.step) .fontSize(10) } .width('80%') } .width('100%') } class MySliderStyle implements ContentModifier<SliderConfiguration> { /** MARK: 成员变量 */ showSlider: boolean = true sliderChangeMode: number = 0 /** MARK: 构造函数 */ constructor(showSlider: boolean, sliderChangeMode: number) { this.showSlider = showSlider this.sliderChangeMode = sliderChangeMode } /** MARK: 自定义内容 */ applyContent(): WrappedBuilder<[SliderConfiguration]> { return wrapBuilder(buildSlider) } } @Entry @Component struct SliderExample { /** MARK: 成员变量 */ @State showSlider: boolean = true @State sliderValue: number = 0 @State sliderMin: number = 10 @State sliderMax: number = 100 @State sliderStep: number = 20 @State sliderChangeMode: number = 0 /** MARK: 构建函数 */ build() { Column({ space: 8 }) { Row() { Slider({ value: this.sliderValue, min: this.sliderMin, max: this.sliderMax, step: this.sliderStep, }) .showSteps(true) .onChange((value: number, mode: SliderChangeMode) => { this.sliderValue = value this.sliderChangeMode = mode console.info('【SliderLog】value:' + value + 'mode:' + mode.toString()) }) .contentModifier(new MySliderStyle(this.showSlider, this.sliderChangeMode)) } .width('100%') }.width('100%') } }