开天辟地 HarmonyOS(鸿蒙) - 组件(文本类): Text(文本显示框)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

开天辟地 HarmonyOS(鸿蒙) - 组件(文本类): Text(文本显示框)

示例如下:

pages\component\text\TextDemo.ets

/*
 * Text - 文本显示框
 */

import { MyLog, TitleBar } from '../../TitleBar'
import { LengthMetrics } from '@kit.ArkUI'
import { font } from '@kit.ArkUI'

@Entry
@Component
struct TextDemo {

  build() {
    Column({ space: 10 }) {
      TitleBar()

      Tabs() {
        TabContent() { MySample1() }.tabBar('基础').align(Alignment.Top)
        TabContent() { MySample2() }.tabBar('内容选择').align(Alignment.Top)
        TabContent() { MySample3() }.tabBar('自定义字体').align(Alignment.Top)
        TabContent() { MySample4() }.tabBar('交互').align(Alignment.Top)
      }
      .scrollable(true)
      .barMode(BarMode.Scrollable)
    }
  }
}

@Component
struct MySample1 {
  build() {
    Column({space: 10}) {
      /*
       * Text - 文本显示框
       *   content - 需要显示的内容,如果来自 resource 则在 /entry/src/main/resources/base/element/string.json 配置
       *   fontColor() - 字体颜色
       *   font() - 可以指定 size, weight(取值在 100 - 900 之间,取值越大字体越粗,默认值为 400), style, family
       *   fontSize() - 字体大小(设置了此值,则 font 的 size 无效)
       *   fontWeight() - 字体粗细(设置了此值,则 font 的 weight 无效)
       *     取值在 100 - 900 之间,取值越大字体越粗,默认值为 400
       *   fontStyle() - 字体斜体(设置了此值,则 font 的 style 无效)
       *   fontFamily() - 字体(设置了此值,则 font 的 family 无效)
       *   decoration() - 下划线/中划线/上划线
       *     type - 类型(TextDecorationType 枚举)
       *       None - 无
       *       Underline - 下划线
       *       Overline - 上划线
       *       LineThrough - 中划线
       *     color - 颜色
       *     style - 样式(TextDecorationStyle 枚举)
       *       SOLID - 实线
       *       DOTTED - 点状线
       *       DASHED - 虚线
       *       WAVY - 波浪线
       *   textShadow() - 文字阴影(ShadowOptions 对象或者 ShadowOptions 对象集合)
       *     radius - 阴影的模糊半径
       *     color - 阴影的颜色
       *     offsetX - 阴影的 x 轴偏移量
       *     offsetY - 阴影的 y 轴偏移量
       */
      Text($r('app.string.hello_webabcd'))
        .fontColor(Color.Blue)
        .font({
          size: 1,
          weight: FontWeight.Normal,
          style: FontStyle.Normal,
          family: 'HarmonyOS Sans',
        })
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontStyle(FontStyle.Italic)
        .fontFamily('HarmonyOS Sans') // HarmonyOS Sans 是默认字体
        .decoration({
          type: TextDecorationType.Underline,
          color: Color.Red,
          style: TextDecorationStyle.SOLID,
        })
        .textShadow([
          { radius: 5, color: Color.Red, offsetX: 5, offsetY: 5 },
          { radius: 5, color: Color.Green, offsetX: 10, offsetY: 5 },
          { radius: 5, color: Color.Blue, offsetX: 15, offsetY: 5 },
          { radius: 5, color: Color.Orange, offsetX: 20, offsetY: 5 },
          { radius: 5, color: Color.Yellow, offsetX: 25, offsetY: 5 }
        ])

      /*
       * textAlign - 文本的水平对齐方式
       *   Center, Start, End, JUSTIFY(两端对齐)
       * align  - 文本的垂直对齐方式
       *   TopStart, Top, TopEnd, Start, Center, End, BottomStart, Bottom, BottomEnd
       *
       * 注:两端对齐的意思是除了最后一行以外,所有行都会扩展其单词间距以填满整行,且每行的开始和结束都与容器的边缘对齐
       */
      Text($r('app.string.hello_webabcd'))
        .width('100%')
        .height(50)
        .backgroundColor(Color.Orange)
        .fontColor(Color.White)
        .textAlign(TextAlign.End)
        .align(Alignment.Bottom)

      /*
       * textCase - 大小写
       *   Normal, LowerCase, UpperCase
       * baselineOffset - 文本在原位置上的偏移量
       *   大于 0 向上偏移,小于 0 向下偏移
       */
      Text($r('app.string.hello_webabcd'))
        .width('100%')
        .height(50)
        .backgroundColor(Color.Orange)
        .fontColor(Color.White)
        .align(Alignment.Top)
        .textCase(TextCase.UpperCase)
        .baselineOffset(-10)

      /*
       * minFontSize(), maxFontSize() - 最小字体大小和最大字体大小,用于自动调整字体大小以便适应容器大小
       * textIndent() - 首行缩进值
       * maxLines() - 最大行数
       * letterSpacing() - 字符之间的间距
       * lineSpacing() - 行之间的间距
       * lineHeight() - 行高
       * heightAdaptivePolicy() - 字体大小的自适应的方式(TextHeightAdaptivePolicy 枚举)
       *   MAX_LINES_FIRST - 优先根据 maxLines 调整字体大小,其次根据 minFontSize 和 maxFontSize 调整字体大小
       *   MIN_FONT_SIZE_FIRST - 根据 minFontSize 和 maxFontSize 调整字体大小
       *   LAYOUT_CONSTRAINT_FIRST - 优先根据布局约束调整字体大小,其次根据 minFontSize 和 maxFontSize 调整字体大小
       */
      Text('HarmonyOS SDK 是面向鸿蒙原生应用和元服务开发的开放能力合集,提供包括应用框架、应用服务、系统、媒体、AI、图形在内的六大领域丰富完备的开放能力,助力构建焕然一新的鸿蒙原生应用和元服务,带来创新易用的全场景体验。')
        .width('100%')
        .height(80)
        .backgroundColor(Color.Orange)
        .fontColor(Color.White)
        .textAlign(TextAlign.JUSTIFY) // 两端对齐
        .minFontSize(9)
        .maxFontSize(24)
        .maxLines(100)
        .textIndent(20)
        .letterSpacing(1)
        .lineSpacing(LengthMetrics.vp(5))
        .lineHeight(10)
        .heightAdaptivePolicy(TextHeightAdaptivePolicy.MAX_LINES_FIRST)

      /*
       * textOverflow() - 文本溢出后的显示方式
       *   overflow - TextOverflow(枚举)
       *     None - 直接截断
       *     Clip - 直接截断
       *     Ellipsis - 溢出部分用省略号代替
       *     MARQUEE - 跑马灯
       * ellipsisMode() - 有溢出文本并用省略号代替时,省略号的显示位置(TextOverflow 枚举)
       *   START - 省略号显示在开头
       *   CENTER - 省略号显示在中间
       *   END - 省略号显示在结尾
       */
      Text('HarmonyOS SDK 是面向鸿蒙原生应用和元服务开发的开放能力合集,提供包括应用框架、应用服务、系统、媒体、AI、图形在内的六大领域丰富完备的开放能力,助力构建焕然一新的鸿蒙原生应用和元服务,带来创新易用的全场景体验。')
        .width('100%')
        .height(50)
        .fontSize(16)
        .maxLines(2)
        .backgroundColor(Color.Orange)
        .fontColor(Color.White)
        .textOverflow({
          overflow: TextOverflow.Ellipsis
        })
        .ellipsisMode(EllipsisMode.END)

      /*
       * wordBreak() - 断行方式(WordBreak 枚举)
       *   NORMAL - CJK文本在任意2个字符间断行,Non-CJK文本只能在空白符处断行
       *   BREAK_ALL - CJK文本在任意2个字符间断行,Non-CJK文本在任意2个字符间断行
       *   BREAK_WORD - CJK文本在任意2个字符间断行,Non-CJK文本优先在空白字符处断行,如果整行没有空白字符则可以在任意2个字符间断行
       * lineBreakStrategy() - 在 wordBreak 的基础上的断行策略(LineBreakStrategy 枚举)
       *   BALANCED - 尽可能不拆词
       *   HIGH_QUALITY - 在 BALANCED 基础上尽可能填满行(除了最后一行)
       *   GREEDY - 尽可能在每行显示更多的字符
       *
       * CJK文本就是字符之间不需要空行的,类似中文
       * Non-CJK文本就是单词之间需要空行的,类似英文
       */
      Text('HarmonyOS SDK 是面向鸿蒙原生应用和元服务开发的开放能力合集,提供包括应用框架、应用服务、系统、媒体、AI、图形在内的六大领域丰富完备的开放能力,助力构建焕然一新的鸿蒙原生应用和元服务,带来创新易用的全场景体验。')
        .width('100%')
        .fontSize(16)
        .backgroundColor(Color.Orange)
        .fontColor(Color.White)
        .wordBreak(WordBreak.NORMAL)
        .lineBreakStrategy(LineBreakStrategy.BALANCED)
    }
  }
}

@Component
struct MySample2 {

  @State message: string = ''

  // TextController 是用于和 Text 交互的,声明式编程通常都会用这种方式
  controller: TextController = new TextController();

  // 用于扩展 Text 默认的内容选中后的上下文菜单
  // menuItems - 默认的上下文菜单
  // TextMenuItem - 上下文菜单中的某一项
  //   id - 菜单项的标识
  //   content - 菜单项的内容
  //   icon - 菜单项的图标
  ExtendContextMenu(menuItems: Array<TextMenuItem>) {
    // 遍历当前上下文菜单的菜单项
    // 可以通过 id 来识别是什么菜单项
    //   CUT - 剪切
    //   COPY - 复制
    //   PASTE - 粘贴
    //   SELECT_ALL - 全选
    menuItems.forEach((value, index) => {
      if (value.id.equals(TextMenuItemId.COPY)) {
        MyLog.d("复制")
      }
    })
    let item1: TextMenuItem = {
      id: TextMenuItemId.of('item1'),
      content: 'item 1',
      icon: $r('app.media.app_icon'),
    }
    let item2: TextMenuItem = {
      id: TextMenuItemId.of('item2'),
      content: 'item 2',
      icon: $r('app.media.app_icon'),
    }
    menuItems.push(item1) // 在当前上下文菜单的结尾增加一个指定的菜单项
    menuItems.unshift(item2) // 在当前上下文菜单的开头增加一个指定的菜单项
    return menuItems
  }

  // 自定义的 Text 内容选中后的上下文菜单
  // Menu() - 上下文菜单,可以定义多组菜单
  //   MenuItemGroup() - 某一组菜单,可以定义多个菜单项
  //     MenuItem() - 某一菜单项
  //       startIcon - 菜单项起始处的图标
  //       content - 菜单项的内容
  //       onClick() - 菜单项被点击后
  @Builder MyContextMenu() {
    Column() {
      Menu() {
        MenuItemGroup() {
          MenuItem({ startIcon: $r('app.media.app_icon'), content: "menu 1" })
            .onClick((event) => {
              // 关闭指定的菜单
              this.controller.closeSelectionMenu();
            })
          MenuItem({ startIcon: $r('app.media.app_icon'), content: "menu 2" })
          MenuItem({ startIcon: $r('app.media.app_icon'), content: "menu 3" })
        }
        .backgroundColor(Color.Orange)
      }
    }
  }

  build() {
    Column({ space: 30 }) {
      Text(this.message).fontSize(12)

      /*
       * textSelectable() - 是否允许选中 Text 中的内容(TextSelectableMode 枚举)
       *   SELECTABLE_UNFOCUSABLE, SELECTABLE_FOCUSABLE, UNSELECTABLE
       * selection() - 通过指定起始位置和结束位置,设置当前被选中的内容
       * copyOption() - 复制 Text 中被选中的内容时的行为(CopyOptions 枚举)
       *   None - 不支持复制(默认值,此时不允许选中 Text 中的内容)
       *   InApp - 应用内复制
       *   LocalDevice - 设备内复制
       *   CROSS_DEVICE - 跨设备复制
       * onCopy() - 选中的内容复制后触发的事件
       */
      Text('HarmonyOS SDK 是面向鸿蒙原生应用和元服务开发的开放能力合集,提供包括应用框架、应用服务、系统、媒体、AI、图形在内的六大领域丰富完备的开放能力,助力构建焕然一新的鸿蒙原生应用和元服务,带来创新易用的全场景体验。')
        .fontSize(16)
        .textSelectable(TextSelectableMode.SELECTABLE_UNFOCUSABLE)
        .selection(10, 50)
        .copyOption(CopyOptions.InApp)
        .onCopy((value: string) => {
          this.message = '复制的内容:' + value
        })

      /*
       * Text 内可以声明多个 Span, ImageSpan, ContainerSpan, SymbolSpan 子组件,详见相关说明
       * bindSelectionMenu() - 使用自定义的上下文菜单
       *   spanType - 哪种类型的内容被选中后需要弹出自定义的上下文菜单(TextSpanType 枚举)
       *     TEXT - 选中的内容只有文本时
       *     IMAGE - 选中的内容只有图片时
       *     MIXED - 选中的内容既有文本又有图片时
       *   content - 自定义的上下文菜单组件
       *   TextResponseType - 调出上下文菜单的方式(TextResponseType 枚举)
       *     RIGHT_CLICK - 右键
       *     LONG_PRESS - 长按
       *     SELECT - 鼠标选择
       *   options - 选项
       *     onAppear - 自定义菜单弹出时的回调
       *     onDisappear - 自定义菜单关闭时的回调
       *   onTextSelectionChange - 选中内容发生变化时的回调
       *     selectionStart - 选中内容的起始位置
       *     selectionEnd - 选中内容的结束位置
       */
      Text(undefined, { controller: this.controller }) {
        Span('hello webabcd')
        ImageSpan($r('app.media.son'))
          .width(40)
          .height(40)
          .objectFit(ImageFit.Fill)
          .verticalAlign(ImageSpanAlignment.CENTER)
      }
      .fontSize(16)
      .copyOption(CopyOptions.InApp)
      .bindSelectionMenu(TextSpanType.MIXED, this.MyContextMenu, TextResponseType.LONG_PRESS, {
        onAppear: () => {
          this.message = `自定义菜单弹出了`;
        },
        onDisappear: () => {
          this.message = `自定义菜单关闭了`;
        },
      })
      .onTextSelectionChange((selectionStart: number, selectionEnd: number) => {
        this.message = `选中内容发生了变化:selectionStart:${selectionStart}, selectionEnd:${selectionEnd}`;
      })

      /*
       * editMenuOptions() - 扩展默认的上下文菜单
       *   onCreateMenu - 扩展上下文菜单的具体逻辑
       *   onMenuItemClick - 上下文菜单中的某一菜单项被点击后的回调
       *     menuItem - 被点击的菜单项
       *       id - 菜单项的标识,一般通过此值判断是哪个菜单项被点击了
       *         TextMenuItemId.CUT - 剪切
       *         TextMenuItemId.COPY - 复制
       *         TextMenuItemId.PASTE - 粘贴
       *         TextMenuItemId.SELECT_ALL - 全选
       *     textRange - 被选中的内容的范围
       *       start - 起始位置
       *       end - 结束位置
       */
      Text('hello webabcd')
        .fontSize(16)
        .copyOption(CopyOptions.InApp)
        .editMenuOptions({
          onCreateMenu: this.ExtendContextMenu,
          onMenuItemClick: (menuItem: TextMenuItem, textRange: TextRange) => {
            if (menuItem.id.equals(TextMenuItemId.of("item1"))) {
              this.message = "item1 clicked, start:" + textRange.start + "; end:" + textRange.end
              return true;
            }
            if (menuItem.id.equals(TextMenuItemId.COPY)) {
              this.message = "复制按钮被点击了, start:" + textRange.start + "; end:" + textRange.end
              // 返回值代表 handled(是否已被处理)
              //   返回 true 代表已被处理,则不会执行系统的默认逻辑,以本例来说就是选择的内容不会被复制到剪切板
              //   返回 false 代表未处理,则会执行系统的默认逻辑,以本例来说就是选择的内容会被复制到剪切板
              return false;
            }
            return false;
          }
        })
    }
  }
}

@Component
struct MySample3 {
  aboutToAppear() {
    // 把自定义的字体文件放到类似如下的地址 /entry/src/main/resources/rawfile/myfont.otf
    // font.registerFont() - 注册自定义字体
    //   familyName - 指定一个自定义的字体名称
    //   familySrc - 字体文件的地址
    font.registerFont({
      familyName: 'my-font',
      familySrc: $rawfile('myfont.otf') // 真实路径是 /entry/src/main/resources/rawfile/myfont.otf
    })
  }

  build() {
    Column({ space: 10 }) {
      Text("webabcd webabcd").fontSize(36)
        // 指定自定义的字体名称,从而使用已注册的自定义字体
        .fontFamily('my-font')
    }
  }
}

@Component
struct MySample4 {

  // TextController 是用于和 Text 交互的,声明式编程通常都会用这种方式
  controller: TextController = new TextController()

  @State message: string = "abc\nxyz"

  build() {
    Column({ space: 10 }) {

      // 创建 Text 的时候指定 TextController,后续就可以通过这个 TextController 和这个 Text 交互了
      Text('abcdefghijklmnopqrstuvwxyz\nabcdefghijklmnopqrstuvwxyz\nabcdefghijklmnopqrstuvwxyz', {
        controller: this.controller
      })
        .margin({ top: 30 })
        .backgroundColor(Color.Orange)
        .padding(10)
        .fontSize(16)
        .textSelectable(TextSelectableMode.SELECTABLE_UNFOCUSABLE)
        .selection(3, 10)
        .copyOption(CopyOptions.InApp)

      Button('关闭上下文菜单 closeSelectionMenu()')
        .onClick(() => {
          /*
           * TextController - 用于和绑定的 Text 之间的交互
           *   closeSelectionMenu() - 关闭上下文菜单
           */
          this.controller.closeSelectionMenu()
        })

      Button('获取布局相关信息 getLayoutManager()')
        .onClick(() => {
          /*
           * TextController - 用于和绑定的 Text 之间的交互
           *   getLayoutManager() - 获取布局管理对象
           *     getLineCount() - 获取内容的总行数
           *     getLineMetrics() - 获取指定行的布局相关信息
           *       startIndex - 当前行的起始字符,在整个文本中的索引位置
           *       endIndex - 当前行的结束字符,在整个文本中的索引位置
           *       ascent - 基线到当前行的字符顶部的距离(单位 px)
           *       descent - 基线到当前行的字符底部的距离(单位 px)
           *       baseline - 基线到整个文本顶部的距离(单位 px)
           *       topHeight - 当前行的顶部到整个文本的顶部的距离(单位 px)
           *       width - 当前行的宽(单位 px)
           *       height - 当前行的高(单位 px)
           *       left - 当前行的左侧边缘与 Text 组件的左侧边缘之间的距离(单位 px)
           *       lineNumber - 当前行的行索引位置
           */
          let layoutManager: LayoutManager = this.controller.getLayoutManager()

          let lineCount = layoutManager.getLineCount()
          this.message = `lineCount:${lineCount}\n`

          // 获取第 3 行的布局相关信息
          let lineMetrics = layoutManager.getLineMetrics(2)
          this.message += `startIndex:${lineMetrics.startIndex}, endIndex:${lineMetrics.endIndex},
          ascent:${lineMetrics.ascent}, descent:${lineMetrics.descent}, baseline:${lineMetrics.baseline}, topHeight:${lineMetrics.topHeight},
          width:${lineMetrics.width}, height:${lineMetrics.height}, left:${lineMetrics.left}, lineNumber:${lineMetrics.lineNumber}\n`

          let position = layoutManager.getGlyphPositionAtCoordinate(100, 30)
          this.message += `position:${position.position}, affinity:${position.affinity}`
        })

      Text(this.message).fontSize(16)

      Button('更新属性字符串 setStyledString()')
        .onClick(() => {
          /*
           * TextController - 用于和绑定的 Text 之间的交互
           *   setStyledString() - 设置 StyledString(请参见 StyledStringDemo.ets 中的相关说明)
           */
        })
    }
  }
}

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

posted @ 2025-02-05 14:26  webabcd  阅读(243)  评论(0)    收藏  举报