(鸿蒙)实现标题前动态添加标签

1.效果样式

最近在做一个需求,效果图见下图红色框内,标题前放置 标签
注意:
 1.标签包含文本 / 图文 / 单图
 2.标签的数量动态(1个或2个)
 3.标签长度不限制,可能两个标签占满屏幕宽度,即使占满屏幕宽度但是只在一行显示

image.png

2.效果解析

  • 问题1.应采用什么布局实现?
  • 问题2.如何控制标题在超出一行,换行时居左?
  • 问题3.如何控制两个标签占满屏幕宽度时样式?
方案确认
  • 解答1:结合设计,目前排除Colum和Row,暂时无法实现,目前采用Stack层叠布局(也可以使用RelativeContainer相对布局)。

  • 解答2:结合Stack层叠布局 + Text组件的Span进行对为本占位方式(注意需设置为透明),查看组件Text的Api看了下,属性textIndent设置首行文本缩进,大家也可以试一试。

  • 解答3:这里需要计算标签宽度,并检查是否填充父容器的宽度,使用measure.measureText(),如果是图文需要加上图片的宽度,通过constraintSize属性来约束标签的尺寸。

3.代码实现

3.1.单标签(图文) + 标题实现
@Entry
@Component
struct TagTextPage {
  @State tag: string = '回复';
  @State title: string = '通过设计单独的路由模块和动态加载方法,将路由功能抽取为单独的模块供其他模块使用';

  build() {
    Column() {
      Stack({alignContent:Alignment.TopStart}){
        Text(this.tag)
          .fontSize(14)
          .fontColor(Color.White)
          .backgroundColor(Color.Blue)
          .padding(2)
          .onClick(() => {
            console.error('ych' , `点击Tag`)
          })

        Text(this.title){
          //这里注意:如果存在图片,则需要设置ImageSpan并指定width来进行展位,这里使用Span设置width进行展位没有效果。
          Span(this.tag)
            .fontSize(16 )
            .fontColor(Color.Transparent)
          //标题文本
          Span(`${this.title}`)
        }
        .letterSpacing(2)
        .lineHeight(20)
        .fontColor(Color.Black)
        .fontSize(16)
        .margin({left:4})
      }
    }
    .padding({top:40,left:10,right:10,bottom:10})
    .height('100%')
    .width('100%')
  }
}
  • 效果展示:


    image.png
  • 注意:
    • 如果存在图片时,需要在标题的Text组件中添加ImageSpan设置width来进行占位,Span设置width无效果。
    Text(this.title){
      //这里注意:如果存在图片,则需要设置ImageSpan并指定width来进行展位,这里使用Span设置width进行展位没有效果。
      ImageSpan("").width(4)
      Span(this.tag)
          .fontSize(16)
          .fontColor(Color.Transparent)
            
      //标题文本
      Span(`${this.title}`)
    }
    
3.2.两个标签(可能一个/两个,可能一个占满屏幕/可能两个占满屏幕) + 标题实现
@Entry
@Component
struct TagTextPage {
  //标签宽度
  // tag1Title:string = "你好北京"
  tag1Title:string = "你好北京你好北京你好北京你好北京你好北京你好北京你好北京你好北京你好北京你好北京你好北京你好北京你好北京你好北京"
  @State tag1Width: number = 0
  // tag2Title:string = "你好河南"
  tag2Title:string = "你好河南你好河南你好河南你好河南你好河南你好河南你好河南你好河南"
  @State tag2Width: number = 0
  //屏幕宽度(这里也需要考虑边距)
  screenWidth: number = 0
  @State title: string = '通过设计单独的路由模块和动态加载方法,将路由功能抽取为单独的模块供其他模块使用';

  aboutToAppear(): void {
    //容器宽度 = 屏幕宽度 - 边距
    this.screenWidth = display.getDefaultDisplaySync().width - vp2px(20)
  }

  calculateTagWidth(textContext: string):number{
    return MeasureText.measureText({
      textContent: textContext,
      fontSize: `${vp2px(14)}px`    //这里注意需要为px
    })
  }

  //多标签
  build() {
    Column() {
      Stack({alignContent:Alignment.TopStart}){
        if (this.isFullScreenWidth()){
          //充满,那么就需要将两个标签放置在一行,文本从第二行展示
          Row(){
            //标签
            Text(this.tag1Title)
              .fontSize(14)
              .lineHeight(20)
              .backgroundColor(Color.Green)
              .fontColor(Color.White)
              .padding({left:4,right:4})
              .maxLines(1)
              .textOverflow({overflow: TextOverflow.Ellipsis})
              .constraintSize({
                maxWidth: `${this.tag1Width}px`
              })
            Text("").width(4)
            Text(this.tag2Title)
              .fontSize(14)
              .lineHeight(20)
              .backgroundColor(Color.Blue)
              .fontColor(Color.White)
              .padding({left:4,right:4})
              .maxLines(1)
              .textOverflow({overflow: TextOverflow.Ellipsis})
              .constraintSize({
                maxWidth: `${this.tag2Width}px`
              })
          }
          .width('100%')
          .justifyContent(FlexAlign.Start)
          .height(20)


          Text(this.title)
            .letterSpacing(2)
            .lineHeight(20)
            .fontColor(Color.Black)
            .fontSize(14)
            .align(Alignment.TopStart)
            .margin({ top: 21 })
        }else {
          //未充满,那么就和第一种一样,标签和标题放置在一列,文本进行占位缩进
          Stack({alignContent:Alignment.TopStart}){
            Row(){
              //标签
              Text(this.tag1Title)
                .fontSize(14)
                .lineHeight(20)
                .backgroundColor(Color.Green)
                .fontColor(Color.White)
                .padding({left:4,right:4})
                .constraintSize({
                  maxWidth: `${this.tag1Width}px`
                })
              Text("").width(4)
              Text(this.tag2Title)
                .fontSize(14)
                .lineHeight(20)
                .backgroundColor(Color.Blue)
                .fontColor(Color.White)
                .padding({left:4,right:4})
                .constraintSize({
                  maxWidth: `${this.tag2Width}px`
                })
            }
            .width('100%')
            .justifyContent(FlexAlign.Start)
            .height(20)

            //标题缩进
            Text(){
              Span(this.tag1Title)
                .fontSize(14)
                .fontColor(Color.Transparent)
                .padding({left:4,right:4})
              ImageSpan("").width(4)
              Span(this.tag2Title)
                .fontSize(14)
                .fontColor(Color.Transparent)
                .padding({left:4,right:4})

              Span(this.title)
            }.letterSpacing(2)
            .lineHeight(20)
            .fontColor(Color.Black)
            .fontSize(14)
            .margin({left:4})
          }
        }
      }
    }.width('100%')
    .height('100%')
    .padding({top:40,left:10,right:10,bottom:10})
  }

  // 判断所有标签数据的宽度是否充满屏幕
  private isFullScreenWidth():boolean{
    //1.计算第一个标签的宽度(这里需要考虑内边距 和 图片)
    this.tag1Width = this.calculateTagWidth(this.tag1Title) + vp2px(8)
    if (this.tag1Width >= this.screenWidth - vp2px(45) - vp2px(4)) {
      //因为需要两个都显示,所以第一个标签的宽度大于屏幕宽度,那么就让第一个标签的宽度等于 = 屏幕宽度 - 两个标签的间距 - 第二个标签默认的宽度(也就是最小的宽度)
      this.tag1Width = this.screenWidth - vp2px(45) - vp2px(4)
      this.tag2Width = this.screenWidth - this.tag1Width
    }else {
      //2.计算第二个标签的宽度(这里需要考虑内边距 和 图片)
      this.tag2Width = this.calculateTagWidth(this.tag2Title) + vp2px(8)
    }
    //3.第一个标签的宽度 + 第二个标签的宽度 > 屏幕的宽度 ? true : false
    let result = this.tag1Width + this.tag2Width >= this.screenWidth
    if (result) {
      this.tag2Width = this.screenWidth - this.tag1Width
    }
    return result
  }
}
  • 效果展示
    • 1.两个标签均为超过屏幕宽度


      image.png
    • 2.两个标签超过屏幕宽度


      image.png
    • 3.第一个标签超过屏幕宽度,则第二个也需要展示,设置一个默认的最小宽度


      image.png

3.总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容