HarmonyOS鸿蒙Next中如何在应用中创建和使用自定义组件?

HarmonyOS鸿蒙Next中如何在应用中创建和使用自定义组件? 如何在鸿蒙应用中创建可复用的自定义组件?如何在不同页面中使用这些组件?

3 回复

关键字:自定义组件、@Component、组件复用、Props、组件通信

回答

原理解析

自定义组件是ArkTS中实现代码复用和模块化的核心机制。通过[@Component](/user/Component)装饰器创建可复用的UI组件。

核心概念:

  1. @Component装饰器:标记为可复用组件
  2. Props传递:使用[@Prop](/user/Prop)@Link@State等装饰器接收参数
  3. 组件生命周期:aboutToAppearaboutToDisappear
  4. 组件导出:使用export关键字导出组件供其他模块使用

装饰器类型:

  • [@Prop](/user/Prop):从父组件接收只读属性
  • @Link:与父组件双向绑定
  • @State:组件内部状态
  • @Builder:构建函数,用于构建UI片段

详细解决步骤

步骤1:创建基础组件

[@Component](/user/Component)
export struct MyCard {
  title: string = ''
  content: string = ''

  build() {
    Column({ space: 10 }) {
      Text(this.title)
      Text(this.content)
    }
    .padding(15)
    .backgroundColor('FFFFFF')
    .borderRadius(8)
  }
}

步骤2:使用@Prop接收参数

[@Component](/user/Component)
export struct UserCard {
  [@Prop](/user/Prop) userName: string = ''
  [@Prop](/user/Prop) userAge: number = 0

  build() {
    Row() {
      Text(this.userName)
      Text(`${this.userAge}岁`)
    }
  }
}

步骤3:在页面中使用组件

@Entry
[@Component](/user/Component)
struct Index {
  build() {
    Column() {
      MyCard({ title: '标题', content: '内容' })
      UserCard({ userName: '张三', userAge: 25 })
    }
  }
}

示例代码

完整示例:自定义组件库

  1. 卡片组件(CardComponent.ets):
[@Component](/user/Component)
export struct CardComponent {
  [@Prop](/user/Prop) title: string = ''
  [@Prop](/user/Prop) content: string = ''
  [@Prop](/user/Prop) backgroundColor: string = 'FFFFFF'
  [@Prop](/user/Prop) onCardClick?: () => void

  build() {
    Column({ space: 12 }) {
      Text(this.title)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('182431')
      
      Text(this.content)
        .fontSize(14)
        .fontColor('666666')
        .maxLines(3)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
    }
    .width('100%')
    .padding(20)
    .backgroundColor(this.backgroundColor)
    .borderRadius(12)
    .shadow({
      radius: 8,
      color: '1F000000',
      offsetX: 0,
      offsetY: 2
    })
    .onClick(() => {
      if (this.onCardClick) {
        this.onCardClick()
      }
    })
  }
}
  1. 按钮组件(ButtonComponent.ets):
[@Component](/user/Component)
export struct ButtonComponent {
  [@Prop](/user/Prop) text: string = ''
  [@Prop](/user/Prop) type: 'primary' | 'secondary' | 'danger' = 'primary'
  [@Prop](/user/Prop) disabled: boolean = false
  private onClickHandler?: () => void

  build() {
    Button(this.text)
      .width('100%')
      .height(50)
      .fontSize(16)
      .fontWeight(FontWeight.Medium)
      .backgroundColor(this.getBackgroundColor())
      .fontColor(this.getFontColor())
      .borderRadius(8)
      .enabled(!this.disabled)
      .opacity(this.disabled ? 0.5 : 1)
      .onClick(() => {
        if (this.onClickHandler && !this.disabled) {
          this.onClickHandler()
        }
      })
  }

  private getBackgroundColor(): string {
    if (this.disabled) return 'CCCCCC'
    switch (this.type) {
      case 'primary': return '007DFF'
      case 'secondary': return 'F1F3F5'
      case 'danger': return 'FF3B30'
      default: return '007DFF'
    }
  }

  private getFontColor(): string {
    if (this.disabled) return '999999'
    return this.type === 'secondary' ? '182431' : 'FFFFFF'
  }

  // 设置点击事件
  setOnClick(handler: () => void): ButtonComponent {
    this.onClickHandler = handler
    return this
  }
}
  1. 列表项组件(ListItemComponent.ets):
[@Component](/user/Component)
export struct ListItemComponent {
  [@Prop](/user/Prop) icon: string = ''
  [@Prop](/user/Prop) title: string = ''
  [@Prop](/user/Prop) subtitle: string = ''
  [@Prop](/user/Prop) showArrow: boolean = true
  private onItemClick?: () => void

  build() {
    Row({ space: 15 }) {
      if (this.icon) {
        Text(this.icon)
          .fontSize(24)
      }
      
      Column({ space: 5 }) {
        Text(this.title)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .fontColor('182431')
        
        if (this.subtitle) {
          Text(this.subtitle)
            .fontSize(14)
            .fontColor('666666')
        }
      }
      .layoutWeight(1)

      if (this.showArrow) {
        Text('>')
          .fontSize(18)
          .fontColor('CCCCCC')
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('FFFFFF')
    .borderRadius(8)
    .onClick(() => {
      if (this.onItemClick) {
        this.onItemClick()
      }
    })
  }

  setOnClick(handler: () => void): ListItemComponent {
    this.onItemClick = handler
    return this
  }
}
  1. 使用示例(Index.ets):
import { CardComponent } from '../components/CardComponent'
import { ButtonComponent } from '../components/ButtonComponent'
import { ListItemComponent } from '../components/ListItemComponent'

@Entry
[@Component](/user/Component)
struct Index {
  @State cardTitle: string = '欢迎使用'
  @State cardContent: string = '这是一个自定义组件示例'

  build() {
    Column({ space: 20 }) {
      // 使用卡片组件
      CardComponent({
        title: this.cardTitle,
        content: this.cardContent,
        backgroundColor: 'E3F2FD',
        onCardClick: () => {
          console.info('卡片被点击')
        }
      })

      // 使用按钮组件
      ButtonComponent({
        text: '主要按钮',
        type: 'primary'
      }).setOnClick(() => {
        this.cardTitle = '按钮已点击'
        this.cardContent = '内容已更新'
      })

      ButtonComponent({
        text: '次要按钮',
        type: 'secondary'
      }).setOnClick(() => {
        console.info('次要按钮点击')
      })

      ButtonComponent({
        text: '禁用按钮',
        type: 'danger',
        disabled: true
      })

      // 使用列表项组件
      ListItemComponent({
        icon: '⚙️',
        title: '设置',
        subtitle: '应用设置和偏好',
        showArrow: true
      }).setOnClick(() => {
        console.info('设置被点击')
      })

      ListItemComponent({
        icon: 'ℹ️',
        title: '关于',
        subtitle: '版本信息',
        showArrow: true
      })
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('F1F3F5')
  }
}

高级用法

  1. 组件状态管理
[@Component](/user/Component)
export struct CounterComponent {
  @State count: number = 0

  build() {
    Column() {
      Text(`计数: ${this.count}`)
      Button('增加')
        .onClick(() => {
          this.count++
        })
    }
  }
}
  1. 组件事件传递
[@Component](/user/Component)
export struct InputComponent {
  @State inputValue: string = ''
  private onValueChange?: (value: string) => void

  build() {
    TextInput({ text: this.inputValue })
      .onChange((value: string) => {
        this.inputValue = value
        if (this.onValueChange) {
          this.onValueChange(value)
        }
      })
  }

  setOnValueChange(handler: (value: string) => void): InputComponent {
    this.onValueChange = handler
    return this
  }
}
  1. 组件组合
[@Component](/user/Component)
export struct FormComponent {
  [@Prop](/user/Prop) title: string = ''

  build() {
    Column({ space: 15 }) {
      Text(this.title)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      InputComponent()
        .setOnValueChange((value: string) => {
          console.info('输入值:', value)
        })
      
      ButtonComponent({
        text: '提交',
        type: 'primary'
      })
    }
  }
}

常见问题

Q: 如何实现组件间的双向绑定? A: 使用@Link装饰器,父组件传递时使用$符号:ChildComponent({ value: $parentValue })

Q: 组件如何接收函数参数? A: 定义可选的回调函数属性,使用?:标记为可选。

Q: 如何导出组件供其他模块使用? A: 使用export关键字导出组件,在其他文件中使用import导入。

总结:自定义组件是代码复用的基础,掌握组件的创建、参数传递和事件处理是构建可维护应用的关键。

更多关于HarmonyOS鸿蒙Next中如何在应用中创建和使用自定义组件?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中创建自定义组件,需在ets目录下新建.ets文件,使用@Component装饰器定义组件结构,通过@BuilderParam实现动态UI构建。组件内可封装私有状态与布局逻辑,支持@Prop@Link等装饰器进行数据传递。使用时直接导入并声明组件标签,可复用并嵌入到其他页面或组件中。

在HarmonyOS Next中,创建和使用自定义组件是构建应用的基础。以下是具体方法:

1. 创建自定义组件

  • ets目录下新建.ets文件(如MyComponent.ets)。
  • 使用[@Component](/user/Component)装饰器定义组件结构,例如:
    [@Component](/user/Component)
    struct MyComponent {
      @State message: string = 'Hello'
    
      build() {
        Column() {
          Text(this.message)
            .fontSize(20)
        }
      }
    }
    

2. 导出组件

在组件文件中使用export导出,以便其他文件导入:

export { MyComponent }

3. 在其他页面中使用

  • 在需要使用的页面(如Index.ets)中导入组件:
    import { MyComponent } from './MyComponent'
    
  • build()方法中直接调用:
    [@Entry](/user/Entry)
    [@Component](/user/Component)
    struct Index {
      build() {
        Column() {
          MyComponent()
        }
      }
    }
    

4. 组件传参

通过@Prop@Link装饰器实现参数传递:

[@Component](/user/Component)
struct MyComponent {
  @Prop title: string // 父组件向子组件传参

  build() {
    Text(this.title)
  }
}

使用时传递参数:

MyComponent({ title: '自定义标题' })

5. 组件样式复用

  • 样式可提取为单独函数或常量。
  • 支持动态样式绑定。

关键点

  • 组件必须用[@Component](/user/Component)装饰。
  • build()方法返回UI描述。
  • 通过export/import实现模块化。
  • 使用装饰器(@Prop@Link@State等)管理数据状态。

这种方式能有效提高代码复用性和可维护性。

回到顶部