鴻蒙-試一下屬性字符串:除了Span之外,如何在同一個Text組件中展示不同樣式的文字

文章目錄

    • 前言
    • 簡介
    • 有哪些類型
    • 拉出來溜溜
      • Text + Span
      • StyledString
      • 其他
      • CustomSpan
        • 先看一下構造函數
        • onMeasure(measureInfo: CustomSpanMeasureInfo): CustomSpanMetrics
        • onDraw(context: DrawContext, drawInfo: CustomSpanDrawInfo)
    • 遺留問題

前言

在開發中,經常會遇到一段文字中需要設置不同的字體樣式和點擊事件,最常見的就是在"我已仔細閱讀并同意《隱私政策》和《用戶協議》"這種情況,需要將書名號中的文字高亮,并且在點擊的時候需要跳轉到不同頁面。一般我們可以使用Text+Span來實現,但我們還有另外一種方法:屬性字符串

簡介

方便靈活應用文本樣式的對象,可通過TextController中的setStyledString方法與Text組件綁定,可通過RichEditorStyledStringController中的setStyledString方法與RichEditor組件綁定。
但需要注意以下幾點:

  • 當組件樣式和屬性字符串中的樣式沖突時,沖突部分以屬性字符串設置的樣式為準,未沖突部分則生效組件的樣式。
  • 當屬性字符串和Text子組件沖突時,屬性字符串優先級高,即當Text組件中綁定了屬性字符串,忽略Text組件下包含Span等子組件的情況。
  • 不支持@State修飾。
  • 建議將StyledString定義為成員變量,從而避免應用退后臺后被銷毀。
  • 目前不支持在worker線程中使用。

最重要的一點,文檔上沒提到的:在aboutToAppear生命周期中調用textController.setStyledString()是沒有效果的的。
著也是為什么文檔中的示例將該方法的調用放在onPageShow方法的原因。
當然也可以在組件的onAppear方法中調用

有哪些類型

一般情況下,MutableStyledString使用的多一些。該類繼承自StyledString,其構造方法如下

 constructor(value: string | ImageAttachment | CustomSpan , styles?: Array<StyleOptions>)

一般情況下我們是這么使用的

//創建無樣式屬性的字符串,然后調用該對象的 appendStyledString insertStyledString 等方法設置各種屬性
let mutableStyledString:MutableStyledString = new MutableStyledString("字符串")//直接添加各種樣式屬性
let mutableStyledString:MutableStyledString = new MutableStyledString("字符串",[{start:2,length:2,styledKey:StyledStringKey.DECORATION,styledValue:new DecorationStyle({color:Color.Red,type:TextDecorationType.LineThrough,style:TextDecorationStyle.WAVY})}])

這里的styledKey和styledValue是需要一一對應的,當這兩個值不匹配時不生效。
比如:

StyledStringKey.FONT <-> TextStyle
StyledStringKey.DECORATION <-> DecorationStyle
StyledStringKey.BASELINE_OFFSET <-> BaselineOffsetStyle
StyledStringKey.LETTER_SPACING <-> LetterSpacingStyle
StyledStringKey.TEXT_SHADOW <-> TextShadowStyle
StyledStringKey.LINE_HEIGHT <-> LineHeightStyle
StyledStringKey.BACKGROUND_COLOR <-> BackgroundColorStyle
StyledStringKey.URL <-> UrlStyle
StyledStringKey.GESTURE <-> GestureStyle
StyledStringKey.PARAGRAPH_STYLE <-> ParagraphStyle
StyledStringKey.USER_DATA <-> extends UserDataSpan

還有兩個比較特殊的:StyledStringKey.CUSTOM_SPANStyledStringKey.IMAGE,這兩個用的比較少。

拉出來溜溜

來看下如何實現一開始說的那個例子

Text + Span

Text(){Span("我已仔細閱讀并同意").fontColor("#333333").fontSize(16)Span("《用戶協議》").fontColor("#39d175").fontSize(16).onClick((_)=>{promptAction.showToast({message:"打開用戶協議頁面"})})Span("和").fontColor("#333333").fontSize(16)Span("《隱私協議》").fontColor("#39d175").fontSize(16).onClick((_)=>{promptAction.showToast({message:"打開隱私協議頁面"})})
}

StyledString

      Text(undefined,{controller:this.protocolTextController}).onAppear(()=>{let protocolStyledString : MutableStyledString = new MutableStyledString("我已仔細閱讀并同意《用戶協議》和《隱私協議》",[{start: 9,length: 6,styledKey: StyledStringKey.FONT,styledValue: new TextStyle({fontColor:"#39d175",fontSize:LengthMetrics.fp(16),})},{start: 9,length: 6,styledKey: StyledStringKey.GESTURE,styledValue: new GestureStyle({onClick:(event:ClickEvent)=>{promptAction.showToast({message:"打開用戶協議頁面"})},onLongPress:(event:GestureEvent)=>{}})},{start: 16,length: 6,styledKey: StyledStringKey.FONT,styledValue: new TextStyle({fontColor:"#39d175",fontSize:LengthMetrics.fp(16),})},{start: 16,length: 6,styledKey: StyledStringKey.GESTURE,styledValue: new GestureStyle({onClick:(event:ClickEvent)=>{promptAction.showToast({message:"打開隱私協議頁面"})},onLongPress:(event:GestureEvent)=>{}})}])this.protocolTextController.setStyledString(protocolStyledString)})

當然這么比較起來還是Text+Span比較簡潔。但當遇到Span不支持的屬性的時候,還是得用StyledString,比如設置背景色、下劃線、刪除線、偏移、字間距等等

其他

整個全乎的看下效果

    this.mutableStyledString = new MutableStyledString("豫章故郡,洪都新府。星分翼軫,地接衡廬。襟三江而帶五湖,控蠻荊而引甌越。物華天寶,龍光射牛斗之墟;人杰地靈,徐孺下陳蕃之榻。", [{start: 0,length: 6,styledKey: StyledStringKey.FONT,styledValue:  new TextStyle({ fontColor: Color.Blue })}, {start: 7,length: 6,styledKey: StyledStringKey.DECORATION,styledValue: new DecorationStyle({color: Color.Red,type: TextDecorationType.LineThrough,style: TextDecorationStyle.WAVY})}, {start: 14,length: 6,styledKey: StyledStringKey.BASELINE_OFFSET,styledValue: new BaselineOffsetStyle(new LengthMetrics(6, LengthUnit.VP))}, {start: 21,length: 6,styledKey: StyledStringKey.LETTER_SPACING,styledValue: new LetterSpacingStyle(new LengthMetrics(6, LengthUnit.VP))}, {start: 28,length: 6,styledKey: StyledStringKey.TEXT_SHADOW,styledValue: new TextShadowStyle({radius: 5,type: ShadowType.COLOR,color: Color.Yellow,offsetX: 10,offsetY: -10})}, {start: 35,length: 6,styledKey: StyledStringKey.LINE_HEIGHT,styledValue: new LineHeightStyle(LengthMetrics.fp(20))}// , {//   start: 42,//   length: 6,//   styledKey: StyledStringKey.BACKGROUND_COLOR,//   styledValue: new BackgroundColorStyle({//     color: Color.Pink,//     radius: 6//   })// }// , {//   start: 49,//   length: 6,//   styledKey: StyledStringKey.URL,//   styledValue: new UrlStyle("https://www.example.com")// }, {start: 56,length: 6,styledKey: StyledStringKey.PARAGRAPH_STYLE,styledValue:new ParagraphStyle({ textAlign: TextAlign.End, maxLines: 1, wordBreak: WordBreak.BREAK_ALL, overflow: TextOverflow.Ellipsis})}]);

注意:BackgroundColorStyleUrlStyle是api14開始支持的

在這里插入圖片描述

CustomSpan

我們需要繼承CustomSpan并重寫onMeasure(measureInfo: CustomSpanMeasureInfo): CustomSpanMetrics來完成測量,重寫onDraw(context: DrawContext, options: CustomSpanDrawInfo)來完成繪制,這和自定義組件的自定義布局中重寫onMeasureSizeonPlaceChildren差不多。

先看一下構造函數

CustomSpan對象只有一個無參構造函數,但一般情況下我們需要在構造函數中傳入我們需要的參數,大多數情況我們需要傳入要繪制的內容,這里簡單的以繪制字符串為例。還需要一個UIContext的上下文對象,用于獲取各種工具。
另外我們還需要根據需求,定義一些變量,來保存我們需要使用的參數。這里我們需要保存字體大小

class MyCustomSpan extends CustomSpan {constructor(text: string, uiContext: UIContext) {super();this.text = text;this.uiContext = uiContext}text: stringuiContext: UIContextfontSizeFp:number =0}
onMeasure(measureInfo: CustomSpanMeasureInfo): CustomSpanMetrics

這個方法中,我們可以獲取到文字大小,需要返回一個CustomSpanMetrics對象,表示自定義繪制Span的尺寸。

  onMeasure(measureInfo: CustomSpanMeasureInfo): CustomSpanMetrics {// measureInfo.fontSize單位為fpthis.fontSizeFp = measureInfo.fontSize// 傳入的fontSize單位為fp,返回文本所占布局寬度和高度單位均為px。let size =this.uiContext.getMeasureUtils().measureTextSize({ textContent: this.text, fontSize: measureInfo.fontSize })//customSpanMetrics的width和height 單位為vpthis.customSpanMetrics = { width: px2vp(size.width as number), height: px2vp(size.height as number) };return this.customSpanMetrics}

這樣我們就獲取到了Span的尺寸信息

onDraw(context: DrawContext, drawInfo: CustomSpanDrawInfo)

DrawContext的實例對象context中的size屬性保存的畫布的寬高,canvas屬性保存了畫布對象;這里需要注意的是:獲取的畫布是Text組件的畫布,繪制時不會超出Text組件的范圍。這里就先認為是屬性的,戳進去看源碼就是定義的get方法:get canvas(): drawing.Canvas;
CustomSpanDrawInfo的實例對象drawInfo則保存了自定義繪制Span的繪制信息。比如屬性x自定義繪制Span相對于掛載組件的偏移
lineTop自定義繪制Span相對于Text組件的上邊距lineBottom自定義繪制Span相對于Text組件的下邊距baseline自定義繪制Span的所在行的基線偏移量,它們的單位是都px

  onDraw(context: DrawContext, drawInfo: CustomSpanDrawInfo): void {console.error(`onDraw drawInfo x:${drawInfo.x}  lineTop:${drawInfo.lineTop}  lineBottom:${drawInfo.lineBottom}  baseline:${drawInfo.baseline}`)console.error(`onDraw context ${vp2px(context.size.width)}   ${vp2px(context.size.height)}`)let canvas = context.canvas;const font = new drawing.Font();font.setSize(vp2px(this.fontSizeFp));const brush = new drawing.Brush();brush.setColor({alpha: 255,red: 0,green: 74,blue: 175});canvas.attachBrush(brush)const textBlob = drawing.TextBlob.makeFromString(this.text, font, drawing.TextEncoding.TEXT_ENCODING_UTF8);canvas.drawTextBlob(textBlob, drawInfo.x, drawInfo.baseline);canvas.detachBrush()}

這樣我們就完成了一個簡單的自繪制的Span。

遺留問題

但是這里有個很大的問題:當繪制的文字多的時候,文字并不會換行。因為我們測量出來文字是按一行計算的,高度也是一行文字的高度。

想要計算需要幾行,就需要知道Text組件的寬度。這里可以從構造函數中傳進來。

那么問題就變成了如何獲取Text組件的寬度?可以從onAreaChange回調中獲取,但這個函數并不可靠,有時候一步小心使用屬性字符串時返回的寬度就是0。

另外一個問題就是,我們如何知道組件的寬度可以放下幾個字?假如一行可以放下5.4個字,那實際結果肯定是一行只繪制5個字。

我們可以根據這個方法來計算需要多大的高度。

還有一個問題就是在onDraw方法中drawInfo.baseLine屬性,目前來看就是最后一行文字的baseLine,如果有多行文字,還需要我們自己計算每一行的baseLine


哈哈,遺留的問題有時間再說吧,這個自定義繪制Span用的機會應該不大。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/902963.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902963.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902963.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Nginx 安裝與配置全流程指南(2025 最新版)

一、環境準備與依賴安裝 1.1 系統要求 操作系統&#xff1a;支持主流 Linux 發行版&#xff08;Ubuntu 20.04/CentOS 7/Debian 10&#xff09;硬件配置&#xff1a;內存 ≥512MB&#xff0c;磁盤 ≥10GB 可用空間&#xff08;建議使用 SSD&#xff09;網絡要求&#xff1a;開…

【LeetCode 熱題 100】滑動窗口最大值 / 最小覆蓋子串 / 輪轉數組 / 缺失的第一個正數

??個人主頁&#xff1a;小羊 ??所屬專欄&#xff1a;LeetCode 熱題 100 很榮幸您能閱讀我的文章&#xff0c;誠請評論指點&#xff0c;歡迎歡迎 ~ 目錄 子串和為 K 的子數組滑動窗口最大值最小覆蓋子串 普通數組最大子數組和合并區間輪轉數組除自身以外數組的乘積缺失的…

golang的cgo的一點小心得

最后有個項目需要涉及到cgo&#xff0c;在這塊以前用的不多&#xff0c; 這次略微用得深入了一點&#xff0c;記下來幾點以備以后使用 本質上cgo去用的時候就是遵守一些ABI而已&#xff0c;總體而言&#xff0c;盡量避免復雜結構的來回傳遞。1 對于變長參數&#xff0c;只有…

異構網絡環境下的切換策略研究

移動互聯網應用快速崛起,現有的無線接入技術有,無線局域網(Wireless Local Area NetWork,WLAN),移動蜂窩網絡(4G,5G),無線廣域網(Wireless Wide Area Network,WWAL)以及衛星通信網絡等。多接入技術方便用戶通信,還符合多業務場景。這種多無線接入技術共存的網絡環…

人工智能賦能美妝零售數字化轉型:基于開源AI大模型的S2B2C商城系統構建

摘要 在消費升級背景下&#xff0c;美妝行業正經歷從傳統賣場向智能體驗空間的轉型。本文以"未來商店"為研究對象&#xff0c;探討開源AI大模型與S2B2C商城系統的協同效應&#xff0c;揭示人工智能技術如何重構"人-貨-場"關系。通過實證研究發現&#xff…

計算機視覺中的正則化:從理論到實踐的全面解析

&#x1f31f; 計算機視覺中的正則化&#xff1a;從理論到實踐的全面解析&#x1f31f; 大家好&#xff01;今天要和大家分享的是在計算機視覺&#xff08;CV&#xff09;領域中非常重要的一個概念——正則化&#xff08;Regularization&#xff09;。無論你是剛開始接觸深度學…

Linux字符設備驅動開發的詳細步驟

1. 確定主設備號?? ??手動指定??&#xff1a;明確設備號時&#xff0c;使用register_chrdev_region()靜態申請&#xff08;需確保未被占用&#xff09;。??動態分配??&#xff1a;通過alloc_chrdev_region()由內核自動分配主設備號&#xff08;更靈活&#xff0c;推…

軟件工程效率優化:一個分層解耦與熵減驅動的系統框架

軟件工程效率優化&#xff1a;一個分層解耦與熵減驅動的系統框架** 摘要 (Abstract) 本報告構建了一個全面、深入、分層的軟件工程效率優化框架&#xff0c;旨在超越簡單的技術羅列&#xff0c;從根本的價值驅動和熵減原理出發&#xff0c;系統性地探討提升效率的策略與實踐。…

【Docker游戲】使用Docker部署vue-XiuXianGame文字修仙小游戲

【Docker游戲】使用Docker部署vue-XiuXianGame文字修仙小游戲 一、vue-XiuXianGame介紹1.1 vue-XiuXianGame簡介1.2 主要特點 二、本次實踐規劃2.1 本地環境規劃2.2 本次實踐介紹 三、本地環境檢查3.1 檢查Docker服務狀態3.2 檢查Docker版本3.3 檢查docker compose 版本 四、拉…

用 LangChain 手搓 RAG 系統:從原理到實戰

一、RAG 系統簡介 在當今信息爆炸的時代&#xff0c;如何高效地從海量數據中獲取有價值的信息并生成準確、自然的回答&#xff0c;成為了人工智能領域的重要課題。檢索增強生成&#xff08;Retrieval-Augmented Generation&#xff0c;RAG&#xff09;系統應運而生&#xff0c;…

SpringBoot集成LiteFlow實現輕量級工作流引擎

LiteFlow 是一款專注于邏輯驅動流程編排的輕量級框架&#xff0c;它以組件化方式快速構建和執行業務流程&#xff0c;有效解耦復雜業務邏輯。通過支持熱加載規則配置&#xff0c;開發者能夠即時調整流程步驟&#xff0c;將復雜的業務如價格計算、下單流程等拆分為獨立且可復用的…

38 python random

在實際中,我們常常會用到隨機的概念,比如 模擬抽獎活動(如:月度優秀員工抽獎)生成測試數據(如:隨機考勤時間、隨機銷售額)打亂數據順序(如:隨機分配任務到人)Python 的random模塊就像你的 "隨機事件生成器",幫你輕松創建各種隨機數據 一、基礎操作:從隨…

附贈二張圖,闡述我對大模型的生態發展、技術架構認識。

文章精煉&#xff0c;用兩張圖說明大模型發展業態方向&#xff0c;以及大模型主體技術架構。&#xff08;目前還需要進一步驗證我的Thought && ideas&#xff0c;等待機會吧.........&#xff09; 圖一&#xff1a;探究大模型三個層次應用方向&#xff0c;淺層次入門簡…

2025上海車展 | 移遠通信全棧車載智能解決方案重磅亮相,重構“全域智能”出行新范式

2025年4月23日至5月2日&#xff0c;第二十一屆上海國際汽車工業展覽會在國家會展中心&#xff08;上海&#xff09;盛大啟幕。作為車載智能解決方案領域的領軍企業&#xff0c;移遠通信以“全域智能 馭見未來”為主題&#xff0c;攜豐富的車載解決方案及客戶終端驚艷亮相8.2館8…

告別 “幻覺” 回答:RAG 中知識庫與生成模型的 7 種對齊策略

一、引言 大語言模型&#xff08;LLM&#xff09;在文本生成領域展現出驚人能力&#xff0c;但 “幻覺” 問題&#xff08;生成虛構或偏離事實的內容&#xff09;始終是落地應用的核心挑戰。檢索增強生成&#xff08;RAG&#xff09;通過將外部知識庫與 LLM 結合&#xff0c;形…

項目筆記2:post請求是什么,還有什么請求

在 HTTP&#xff08;超文本傳輸協議&#xff09;中&#xff0c;請求方法用于向服務器表明客戶端想要執行的操作。POST 請求是其中一種常見的請求方法&#xff0c;此外還有 GET、PUT、DELETE 等多種請求方法&#xff0c;下面為你詳細介紹&#xff1a; POST 請求 定義&#xff…

中間系統-鄰居建立,數據庫同步

ISIS鄰居狀態&#xff1a; 1、Down&#xff1a;接口一旦啟用ISIS協議之后就是Down狀態 2、Init&#xff1a;收到了鄰居的Hello報文后&#xff0c;發現了鄰居。 3、up&#xff1a;收到了鄰居的Hello報文&#xff0c;并且在鄰居的hello報文中發現了自己。 ISIS鄰居建立的條件&…

玩轉Docker | Docker部署LMS輕量級音樂工具

玩轉Docker | Docker部署LMS輕量級音樂工具 前言一、LMS介紹LMS簡介主要特點二、系統要求環境要求環境檢查Docker版本檢查檢查操作系統版本三、部署LMS服務下載鏡像創建容器創建容器檢查容器狀態檢查服務端口安全設置四、訪問LMS服務訪問LMS首頁注冊賬號五、基本使用上傳音樂文…

AR行業應用案例與NXP架構的結合

1. 工業巡檢AR頭盔 場景示例&#xff1a;寧德核電基地使用AR智能頭盔進行設備巡檢&#xff0c;通過實時數據疊加和遠程指導&#xff0c;將工作效率提升35%。頭盔需處理傳感器數據、圖像渲染和低延遲通信1。 NXP架構支持&#xff1a; 協處理器角色&#xff1a;NXP i.MX RT系列M…

【Harmony OS】組件

目錄 組件概述 組件常用屬性 系統內置組件 Text TextArea 多行文本輸入框組件 TextInput 文本輸入框 Button Image 圖片組件&#xff0c;支持本地圖片和網絡圖片 Radio 單選框 Checkbox 復選框 Blank 空白填充組件 Divider 分隔符 PatternLock 圖案密碼鎖組件 Prog…