鸿蒙开发笔记-21-ArkUI:按钮、单选框、切换按钮、进度条

334 阅读2分钟

ArkUI作为HarmonyOS的UI开发框架,提供了丰富的组件库帮助开发者快速构建应用界面。本文将介绍四个常用基础组件——按钮(Button)单选框(Radio)切换按钮(Toggle)进度条(Progress)

一、按钮组件 (Button)

按钮是交互界面的核心元素,ArkUI提供了灵活的按钮创建方式和样式定制能力。

属性类型作用
typeButtonType控制样式(Normal/Capsule/Circle)
stateEffectboolean启用点击反馈特效
borderRadiusnumber圆角大小(仅Normal类型有效)
  • 重要操作建议使用stateEffect: true
  • 禁用状态通过disabled: true实现

创建基础按钮

ArkUI提供三种内置按钮类型,每种类型有不同的默认样式和定制能力:

类型特点使用场景
Capsule胶囊型(默认),圆角自动为高度一半主要操作按钮
Circle圆形按钮,宽高需一致悬浮操作按钮(FAB)
Normal矩形按钮,支持自定义圆角次要操作或文本按钮

Button组件支持两种创建方式:文本按钮和包含子组件的复合按钮。

// 1. 文本按钮
Button('确认', { type: ButtonType.Normal, stateEffect: true })
  .borderRadius(8) //圆角大小(仅Normal类型有效)
  .backgroundColor(0x317aff)
  .width(90)
  .height(40)
//	stateEffect:启用点击反馈特效
        
// 2. 包含子组件的按钮
Button({ type: ButtonType.Capsule, stateEffect: true }) {
  Row() {
    Image($r('app.media.ic_arrow_right')).width(20).height(20)
    Text('下一步').fontSize(16).fontColor(Color.White)
  }.alignItems(VerticalAlign.Center)
}.width(150).height(45).backgroundColor(0x007DFF)
  • 样式定制示例:
// API 11+ 新增按钮样式
Button('强调按钮')
  .buttonStyle(ButtonStyleMode.EMPHASIZED) // 强调样式
  .controlSize(ControlSize.LARGE) // 大型按钮
  .backgroundColor(0xFF007A)

Button('文字按钮')
  .buttonStyle(ButtonStyleMode.TEXT) // 文字样式(无背景)
  .fontColor(0x007DFF)
  
  
  • 渐变背景按钮
Button('渐变')
.linearGradient({
  angle: 90,
  colors: [['#FF416C', 0.1], ['#FF4B2B', 0.9]]
})
.borderRadius(20)
.height(45)

事件处理

按钮最常用的事件是onClick点击事件:

Button('点击提示')
  .onClick(() => {
    promptAction.showToast({ message: '按钮被点击' })
  })

按钮状态管理

通过状态变量实现按钮的动态效果:

@State isFollowed: boolean = false

Button(this.isFollowed ? '已关注' : '关注')
  .backgroundColor(this.isFollowed ? 0xCCCCCC : 0x007DFF)
  .onClick(() => {
    this.isFollowed = !this.isFollowed
  })
  
// 使用状态变量控制按钮行为
@State isDisabled: boolean = false

Button('禁用状态')
.enabled(!this.isDisabled)
.backgroundColor(this.isDisabled ? '#CCCCCC' : '#2196F3')
.onClick(() => this.isDisabled = true)

二、单选框组件 (Radio)

单选框用于从一组选项中选择唯一选项,需通过group属性实现互斥选择。

基础用法

Column() {
  Radio({ value: 'opt1', group: 'theme' })
    .checked(true)
    .onChange((isChecked) => {
      if (isChecked) console.log('选择了浅色主题')
    })
  Text('浅色主题')
  
  Radio({ value: 'opt2', group: 'theme' })
    .onChange((isChecked) => {
      if (isChecked) console.log('选择了深色主题')
    })
  Text('深色主题')
}

注意:同一组单选框必须设置相同的group值才能实现互斥选择。

自定义样式

API 12+支持通过contentModifier自定义单选框样式:

class CustomRadioModifier implements ContentModifier<RadioConfiguration> {
  applyContent(): WrappedBuilder<[RadioConfiguration]> {
    return wrapBuilder((config: RadioConfiguration) => {
      Image(config.checked ? $r('app.media.radio_checked') : $r('app.media.radio_unchecked'))
        .width(24).height(24)
    })
  }
}

// 使用自定义样式
Radio({ value: 'custom', group: 'style' })
  .contentModifier(new CustomRadioModifier())

实战场景:设置页面选项

@State selectedSound: string = 'ring'

Column() {
  Text('通知铃声').fontSize(16).margin(10)
  
  Row({ space: 10 }) {
    Radio({ value: 'ring', group: 'sound' })
      .checked(this.selectedSound === 'ring')
      .onChange((isChecked) => {
        if (isChecked) this.selectedSound = 'ring'
      })
    Text('系统铃声')
    
    Radio({ value: 'vibrate', group: 'sound' })
      .checked(this.selectedSound === 'vibrate')
      .onChange((isChecked) => {
        if (isChecked) this.selectedSound = 'vibrate'
      })
    Text('振动模式')
  }
}

三、切换按钮 (Toggle)

三种切换样式

类型适用场景默认样式
Button模式切换(如夜间模式)文字标签
Checkbox多选简化版方形复选框
Switch设备开关圆形滑块
  • 示例
Column({ space: 20 }) {
  // 1. 复选框(API 11+默认圆形)
  Toggle({ type: ToggleType.Checkbox, isOn: false })
    .selectedColor(Color.Pink)
  
  // 2. 开关
  Toggle({ type: ToggleType.Switch, isOn: true })
    .selectedColor(Color.Green)
    .switchPointColor(Color.White)
  
  // 3. 状态按钮
  Toggle({ type: ToggleType.Button, isOn: false }) {
    Text('夜间模式')
      .fontSize(14)
  }.width(120)
  .selectedColor(Color.Blue)
}

事件响应

通过onChange事件监听状态变化:

@State bluetoothOn: boolean = false

Toggle({ type: ToggleType.Switch, isOn: this.bluetoothOn })
  .onChange((isOn) => {
    this.bluetoothOn = isOn
    if (isOn) {
      promptAction.showToast({ message: '蓝牙已开启' })
    }
  })

样式定制

// 自定义开关颜色和大小
Toggle({ type: ToggleType.Switch, isOn: true })
  .selectedColor(Color.Orange)    // 开启状态颜色
  .switchPointColor(Color.Yellow) // 滑块颜色
  .width(60)                     // 开关宽度
  .height(30)                    // 开关高度

四、进度条组件 (Progress)

进度条用于展示操作进度,支持多种视觉样式和动态更新。

五种进度条类型

类型特点适用场景
Linear直线进度文件下载、内容加载
Ring无刻度圆环简单进度展示
ScaleRing带刻度圆环精准进度显示
Eclipse实心圆形完成率展示
Capsule胶囊形状数据上传/下载
Column({ space: 15 }) {
  // 1. 线性进度条
  Progress({ value: 30, total: 100, type: ProgressType.Linear })
    .width(200)
  
  // 2. 环形无刻度进度条
  Progress({ value: 40, total: 100, type: ProgressType.Ring })
    .width(80).height(80)
    .style({ strokeWidth: 8 })
  
  // 3. 环形有刻度进度条
  Progress({ value: 60, total: 100, type: ProgressType.ScaleRing })
    .width(80).height(80)
    .style({ scaleCount: 20, scaleWidth: 3 })
  
  // 4. 圆形进度条
  Progress({ value: 75, total: 100, type: ProgressType.Eclipse })
    .width(80).height(80)
  
  // 5. 胶囊进度条
  Progress({ value: 50, total: 100, type: ProgressType.Capsule })
    .width(150).height(30)
}

动态更新进度

通过状态变量绑定value属性实现动态更新:

@State progress: number = 0

Column() {
  Progress({ value: this.progress, total: 100, type: ProgressType.Capsule })
    .width(200).height(20)
  
  Button('增加进度')
    .onClick(() => {
      if (this.progress < 100) {
        this.progress += 10
      } else {
        this.progress = 0
      }
    })
}

实战场景:文件下载进度

@State downloadProgress: number = 0
private timer: number = -1

aboutToAppear() {
  // 模拟下载进度
  this.timer = setInterval(() => {
    if (this.downloadProgress < 100) {
      this.downloadProgress += 5
    } else {
      clearInterval(this.timer)
    }
  }, 500)
}

build() {
  Column() {
    Progress({ 
      value: this.downloadProgress, 
      total: 100, 
      type: ProgressType.Linear 
    })
    .width('90%')
    
    Text(`下载中: ${this.downloadProgress}%`)
      .fontSize(14)
      .margin(10)
  }
}

五、综合实战:设置页面

下面我们将四个组件组合使用,实现一个设置页面:

@Entry
@Component
struct SettingsPage {
  @State theme: string = 'light'
  @State notifications: boolean = true
  @State bluetooth: boolean = false
  @State volume: number = 60

  build() {
    Scroll() {
      Column({ space: 20 }) {
        // 主题设置(Radio)
        Column() {
          Text('主题设置').fontSize(18).margin(10)
          Row({ space: 20 }) {
            Radio({ value: 'light', group: 'theme' })
              .checked(this.theme === 'light')
              .onChange((isChecked) => {
                if (isChecked) this.theme = 'light'
              })
            Text('浅色')
            
            Radio({ value: 'dark', group: 'theme' })
              .checked(this.theme === 'dark')
              .onChange((isChecked) => {
                if (isChecked) this.theme = 'dark'
              })
            Text('深色')
          }
        }.width('100%').padding(15).backgroundColor(Color.White)

        // 通知设置(Toggle)
        Column() {
          Text('通知设置').fontSize(18).margin(10)
          Row() {
            Text('允许通知')
            Toggle({ type: ToggleType.Switch, isOn: this.notifications })
              .onChange((isOn) => this.notifications = isOn)
          }.justifyContent(FlexAlign.SpaceBetween)
        }.width('100%').padding(15).backgroundColor(Color.White)

        // 蓝牙设置(Toggle)
        Column() {
          Text('蓝牙').fontSize(18).margin(10)
          Row() {
            Text('开启蓝牙')
            Toggle({ type: ToggleType.Switch, isOn: this.bluetooth })
              .onChange((isOn) => this.bluetooth = isOn)
          }.justifyContent(FlexAlign.SpaceBetween)
        }.width('100%').padding(15).backgroundColor(Color.White)

        // 音量调节(Progress)
        Column() {
          Text('媒体音量').fontSize(18).margin(10)
          Progress({ value: this.volume, total: 100, type: ProgressType.Linear })
            .width('100%')
          Row({ space: 10 }) {
            Button('-')
              .width(40).height(40)
              .onClick(() => {
                if (this.volume > 0) this.volume -= 10
              })
            
            Button('+')
              .width(40).height(40)
              .onClick(() => {
                if (this.volume < 100) this.volume += 10
              })
          }.justifyContent(FlexAlign.Center)
        }.width('100%').padding(15).backgroundColor(Color.White)
      }.padding(10).backgroundColor(0xF5F5F5)
    }.backgroundColor(0xF5F5F5)
  }
}

我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 “今阳说” 接收我的最新文章