(鴻蒙)實現(xiàn)標題前動態(tài)添加標簽

1.效果樣式

最近在做一個需求,效果圖見下圖紅色框內(nèi),標題前放置 標簽。
注意:
?1.標簽包含文本 / 圖文 / 單圖
?2.標簽的數(shù)量動態(tài)(1個或2個)
?3.標簽長度不限制,可能兩個標簽占滿屏幕寬度,即使占滿屏幕寬度但是只在一行顯示

image.png

2.效果解析

  • 問題1.應(yīng)采用什么布局實現(xiàn)?
  • 問題2.如何控制標題在超出一行,換行時居左?
  • 問題3.如何控制兩個標簽占滿屏幕寬度時樣式?
方案確認
  • 解答1:結(jié)合設(shè)計,目前排除Colum和Row,暫時無法實現(xiàn),目前采用Stack層疊布局(也可以使用RelativeContainer相對布局)。

  • 解答2:結(jié)合Stack層疊布局 + Text組件的Span進行對為本占位方式(注意需設(shè)置為透明),查看組件Text的Api看了下,屬性textIndent設(shè)置首行文本縮進,大家也可以試一試。

  • 解答3:這里需要計算標簽寬度,并檢查是否填充父容器的寬度,使用measure.measureText(),如果是圖文需要加上圖片的寬度,通過constraintSize屬性來約束標簽的尺寸。

3.代碼實現(xiàn)

3.1.單標簽(圖文) + 標題實現(xiàn)
@Entry
@Component
struct TagTextPage {
  @State tag: string = '回復';
  @State title: string = '通過設(shè)計單獨的路由模塊和動態(tài)加載方法,將路由功能抽取為單獨的模塊供其他模塊使用';

  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){
          //這里注意:如果存在圖片,則需要設(shè)置ImageSpan并指定width來進行展位,這里使用Span設(shè)置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設(shè)置width來進行占位,Span設(shè)置width無效果。
    Text(this.title){
      //這里注意:如果存在圖片,則需要設(shè)置ImageSpan并指定width來進行展位,這里使用Span設(shè)置width進行展位沒有效果。
      ImageSpan("").width(4)
      Span(this.tag)
          .fontSize(16)
          .fontColor(Color.Transparent)
            
      //標題文本
      Span(`${this.title}`)
    }
    
3.2.兩個標簽(可能一個/兩個,可能一個占滿屏幕/可能兩個占滿屏幕) + 標題實現(xiàn)
@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 = '通過設(shè)計單獨的路由模塊和動態(tài)加載方法,將路由功能抽取為單獨的模塊供其他模塊使用';

  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})
  }

  // 判斷所有標簽數(shù)據(jù)的寬度是否充滿屏幕
  private isFullScreenWidth():boolean{
    //1.計算第一個標簽的寬度(這里需要考慮內(nèi)邊距 和 圖片)
    this.tag1Width = this.calculateTagWidth(this.tag1Title) + vp2px(8)
    if (this.tag1Width >= this.screenWidth - vp2px(45) - vp2px(4)) {
      //因為需要兩個都顯示,所以第一個標簽的寬度大于屏幕寬度,那么就讓第一個標簽的寬度等于 = 屏幕寬度 - 兩個標簽的間距 - 第二個標簽?zāi)J的寬度(也就是最小的寬度)
      this.tag1Width = this.screenWidth - vp2px(45) - vp2px(4)
      this.tag2Width = this.screenWidth - this.tag1Width
    }else {
      //2.計算第二個標簽的寬度(這里需要考慮內(nèi)邊距 和 圖片)
      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.第一個標簽超過屏幕寬度,則第二個也需要展示,設(shè)置一個默認的最小寬度


      image.png

3.總結(jié)

  • 確定方案,明確思路,這里的計算需要認真。
  • 組件共性可以通過@Builder裝飾器 / @Styles裝飾器 / @Extend裝飾器進行組件/屬性樣式復用,也可以通過提高代碼的閱讀行和可維護性。
  • @Styles裝飾器 / @Extend裝飾器有一定的局限性,也可以使用屬性修改器AttributeModifier,可以通過Modifier對象動態(tài)修改屬性。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容