【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

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

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

谢谢~~

相关推荐
编程猪猪侠29 分钟前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞33 分钟前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路1 小时前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie1 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_1 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到111 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
风清云淡_A1 小时前
【REACT18.x】CRA+TS+ANTD5.X封装自定义的hooks复用业务功能
前端·react.js
@大迁世界1 小时前
第7章 React性能优化核心
前端·javascript·react.js·性能优化·前端框架