【HarmonyOS】定义一个三段式插槽设计的TitleBar组件

前言

本文章适合看完Harmony官方教程,或者具有一定的前端知识的读者。

基于三段式插槽设计的TitleBar组件

使用插槽设计会让组件变得更加灵活,开发者可以自行配置的预制子元素,也可以按照自己的意愿来定制槽位区域。

如何实现

常见的TitleBar标题居中,左侧返回或者菜单,右侧一到二个按钮。实现的方式很多种,我选择层叠布局(Stack)和弹性布局(Flex)来实现,代码如下:

scss 复制代码
build() {
    Stack() {
      this.content()  //中间槽位
      Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
        this.navigation()  //左侧槽位
        this.actions()  //右侧槽位
      }
    }
  }

通过@BuilderParam装饰器引用@Builder函数,实现插槽和预制子元素

@BuilderParam用来装饰指向@Builder方法的变量,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的功能。 该装饰器用于声明任意UI描述的一个元素,类似slot占位符。 代码如下:

kotlin 复制代码
  @Builder navigationBuilder() {
    Image(this.navigationSrc)
      .width(this.iconWidth)
      .height(this.iconHeight)
      .objectFit(this.iconFit)
      .margin(this.navigationMargin)
      .onClick(() => {
        this.navigationCallback?.()
      })
  }

  @Builder contentBuilder() {
    Text(this.contentText)
      .fontColor(this.contentFontColor)
      .fontSize(this.contentFontSize)
  }

  @Builder actionsBuilder() {
    Row({ space: this.actionSpace }) {
      ForEach(this.actionAttribute, (item: ActionAttribute) => {
        Image(item.src)
          .width(this.iconWidth)
          .height(this.iconHeight)
          .objectFit(this.iconFit)
          .onClick(() => {
            item.callback?.()
          })
      })
    }
    .justifyContent(this.actionAlign)
    .margin(this.actionMargin)
  }

  @BuilderParam navigation: () => void = this.navigationBuilder
  @BuilderParam content: () => void = this.contentBuilder
  @BuilderParam actions: () => void = this.actionsBuilder

效果图

完整代码

kotlin 复制代码
@Component
export default struct TitleBar {
  iconWidth: Length = 25
  iconHeight: Length = 25
  iconFit: ImageFit = ImageFit.Contain
  navigationMargin: Margin | Length = 15
  navigationAlignment: Alignment = Alignment.Start
  navigationSrc: string | PixelMap | Resource = $r("app.media.arrow_left")
  navigationCallback?: () => void = null;
  contentAlignment: Alignment = Alignment.Center
  contentText: string = "";
  contentFontColor: ResourceColor = Color.Black
  contentFontSize: number | string | Resource = 18
  actionMargin: Margin | Length = 15
  actionSpace: string | number = 15
  actionAlign: FlexAlign = FlexAlign.End
  actionAttribute?: Array<ActionAttribute> = null

  @Builder navigationBuilder() {
    Image(this.navigationSrc)
      .width(this.iconWidth)
      .height(this.iconHeight)
      .objectFit(this.iconFit)
      .margin(this.navigationMargin)
      .onClick(() => {
        this.navigationCallback?.()
      })
  }

  @Builder contentBuilder() {
    Text(this.contentText)
      .fontColor(this.contentFontColor)
      .fontSize(this.contentFontSize)
  }

  @Builder actionsBuilder() {
    Row({ space: this.actionSpace }) {
      ForEach(this.actionAttribute, (item: ActionAttribute) => {
        Image(item.src)
          .width(this.iconWidth)
          .height(this.iconHeight)
          .objectFit(this.iconFit)
          .onClick(() => {
            item.callback?.()
          })
      })
    }
    .justifyContent(this.actionAlign)
    .margin(this.actionMargin)
  }

  @BuilderParam navigation: () => void = this.navigationBuilder
  @BuilderParam content: () => void = this.contentBuilder
  @BuilderParam actions: () => void = this.actionsBuilder

  build() {
    Stack() {
      this.content()
      Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
        this.navigation()
        this.actions()
      }
    }
  }
}

class ActionAttribute {
  public src: string | PixelMap | Resource = $r("app.media.icon")
  public callback?: () => void = null;
}

如何使用

scss 复制代码
@Entry
@Component
struct TitleBarPage {
  build() {
    Column({ space: 15 }) {
      TitleBar({
        navigationCallback: () => {
          console.log('导航按钮回调')
        },
        contentText: '默认TitleBar'
      })
        .backgroundColor('#F1F3F5')

      TitleBar({
        navigationSrc: $r("app.media.menu"),
        navigationCallback: () => {
          console.log('导航按钮回调')
        },
        contentText: '更改导航按钮'
      })
        .backgroundColor('#F1F3F5')

      TitleBar({
        navigationCallback: () => {
          console.log('导航按钮回调')
        },
        contentText: '添加活动按钮',
        actionAttribute: [
          {
            src: $r('app.media.browser'),
            callback: () => {
              console.log('活动按钮回调')
            }
          },
          {
            src: $r('app.media.more'),
            callback: () => {
              console.log('活动按钮回调')
            }
          }
        ]
      })
        .backgroundColor('#F1F3F5')

      TitleBar({
        navigationCallback: () => {
          console.log('导航按钮回调')
        },
        content: this.contentBuilder
      })
        .backgroundColor('#F1F3F5')
    }
    .width('100%')
    .height(50)
    .justifyContent(FlexAlign.Center)
  }

  @Builder contentBuilder() {
    Row({ space: 5 }) {
      Text('替换标题插槽')
        .fontColor(Color.Black)
        .fontSize(18)
      Image($r('app.media.arrow_down'))
        .width(15)
        .height(15)
        .objectFit(ImageFit.Contain)
    }
    .onClick(() => {
      console.log('替换标题插槽')
    })
  }
}

Thanks

以上就是本篇文章的全部内容,如有问题欢迎指出,我们一起进步。

如果觉得本篇文章对您有帮助的话请点个赞让更多人看到吧,您的鼓励是我前进的动力。

谢谢~~

相关推荐
_请输入用户名25 分钟前
EventEmitter 是广播,Tapable 是流水线:聊聊它们的本质区别
前端·设计模式
爱学习的茄子26 分钟前
React Fiber:让大型应用告别卡顿的性能革命
前端·react.js·面试
龙在天26 分钟前
我是前端,我来总结一下前端 配 Nginx 的一些案例
前端
Thetimezipsby29 分钟前
基于Taro4打造的一款最新版微信小程序、H5的多端开发简单模板
前端·javascript·微信小程序·typescript·html5·taro
掘金安东尼41 分钟前
前端周刊430期(2025年9月1日–9月7日)
前端
BUG创建者1 小时前
uni 拍照上传拍视频上传以及相册
前端·javascript·音视频
就是帅我不改1 小时前
敏感词过滤黑科技!SpringBoot+Vue3+TS强强联手,打造无懈可击的内容安全防线
前端·vue.js·后端
JackJiang1 小时前
转转客服IM系统的WebSocket集群架构设计和部署方案
前端
codeGoogle1 小时前
大厂研发之谜:千亿投入砸出利润大缩水
前端·人工智能·后端
菲兹园长1 小时前
CSS(展示效果)
前端·javascript·css