当前位置: 首页 > wzjs >正文

广州车陂网站建设公司公司网站建设排名

广州车陂网站建设公司,公司网站建设排名,网站备案是域名备案还是空间备案,wordpress横向导航菜单主题文章目录 限制结果展示思路实现xml.XmlPullParser得到属性值映射 渲染 本来只是需要展示一下简单的富文本,支持简单的背景色,字体大小,字体颜色就够了。调研了一圈都没有完全符合需求的。那就自己撸一个呗。 支持 span、font、br、a标签就好&…

文章目录

    • 限制
    • 结果展示
    • 思路
    • 实现
      • xml.XmlPullParser
      • 得到属性值
      • 映射
    • 渲染

本来只是需要展示一下简单的富文本,支持简单的背景色,字体大小,字体颜色就够了。调研了一圈都没有完全符合需求的。那就自己撸一个呗。
支持 span、font、br、a标签就好,属性的话就支持color、font-color、size、font-size、background、href这些属性就好了。

老规矩,先上效果图

富文本解析与展示

最上方是在浏览器中的表现,手机截图分割线中间是自己撸的控件,最下方是鸿蒙自带的RichText

看起来还行,主要是自己写的,调整起来也方便

限制

  1. 目前只支持了上面说的那些标签级属性:span、font、br、a标签 和 color、font-color、size、font-size、background、href这些属性
  2. 富文本的解析使用的是xml.XmlPullParser,因此对富文本内容中的标签要求比较严格,一定要严格闭合才行,否则解析会失败。
  3. 也要求所有元素都必须包含在标签内容,否则也会失败,应对这个问题,可以通过在富文本最外层添加没有属性的span标签解决
  4. 对于颜色值,只支持了有限了英文名字,建议使用十六进制表示

结果展示

解析结果的展示是Text中嵌套SpanContainerSpan实现的:

  1. Span不支持背景色,要么只能依赖父级控件Text或者父级控件ContainerSpan来设置背景色
  2. ContainerSpan只能包含SpanImageSpan子组件。
  3. SpanImageSpan 没有子控件

也就是说解析结果只有一个Text控件,内容样式都由SpanContainerSpan完成

思路

展示结果使用Text嵌套SpanContainerSpan实现,将样式抽成一个类,要展示的文字作为属性就好了。

export class VNode{text:string=''child?:VNode[]=[]style:Style= new Style()
}/*** 属性默认值都可以做成配置,由调用者传入*/
export class Style{//如果有backGround属性需要使用 ContainerSpanbackgroundColor:string|Resource|Color=Color.TransparentfontSize:number = 16fontColor:string|Resource|Color = Color.BlackhrefFontColor:string|Resource|Color = Color.Bluehref:string | undefined
}

为了方便的话,这里的Style属性默认值可以做成配置的,由调用者传入,方便定制。
如果父级标签设置了背景颜色、文字颜色等属性,子控件没有设置的话,需要继承父标签的属性。如果子标签也设置了属性,则需要覆盖父控件对应的属性。简单来讲就是需要合并子标签和父标签的属性来作为子标签的属性,当然,子标签属性值优先级高于父标签,也就是子标签属性值覆盖父标签的属性值。

就拿截图中的例子来讲:

<span style="color:blue;background:yellow">这位姑娘有一双 <span style="color:blue;background:red"><span style="color:red;background:blue"></span></span>的眼睛
</span>

代码按照xml样式格式化了一下,看着方便
这个字的父级标签设置了背景色为yellow,字体颜色为blue,所以这位姑娘有一双这几个字的背景色就是yellow,字体颜色为blue。但这个字的标签同样设置了背景色为red,优先级要高于父标签,所以这个字的背景色为red
的眼睛是和这位姑娘有一双同级的文字,因此背景色和字体颜色也是一致的。

基于上面的规则,很自然的想到使用Stack来保存每一层级的属性,遇到开始标签则复制一份父控件的样式属性(栈顶元素)然后入栈。遇到结束标签则出栈。注意下br标签,只是换行,不会有样式,直接添加一个Span('\n')就行。

实现

基于我们的需求,需要展示的富文本不会很复杂,最多也就是上面说的这些属性,鸿蒙正好也由自带的xml解析,用来解析富文本也行。
具体文档可以看这里 XML解析

xml.XmlPullParser

这里简单介绍一下流程,
需要导入 import { util, xml } from "@kit.ArkTS"; 这两个包,其中需要用util对富文本进行编码,防止中文和特殊符号乱码。

首先准备好富文本,并且编码一下,创建xml.XmlPullParser对象

// 对数据编码,防止包含中文字符乱码
let textEncoder: util.TextEncoder = new util.TextEncoder();
let arrBuffer: Uint8Array = textEncoder.encodeInto(this.htmlContent);
let xmlPullParser: xml.XmlPullParser = new xml.XmlPullParser(arrBuffer.buffer as object as ArrayBuffer, 'UTF-8');

调用开始解析的方法需要传入一个xml.ParseOptions对象,这个是重点。
这个对象由三个回调方法:tagValueCallbackFunctionattributeValueCallbackFunctiontokenValueCallbackFunction。我们在解析的过程中需要知道标签名(根据标签名解析属性值),标签的开始和结束以及标签包裹的文本。因此我们这里只需要attributeValueCallbackFunctiontokenValueCallbackFunction这两个回调就好了。
先来打印一下解析出来的数据,再决定后面怎么获取属性值

attributeValueCallback(name: string, value: string): boolean {let str = name + ' ' + value + ' ';hilog.error(0x01, 'HtmlParsePage', `attribute  ${str}`)return true; // true:继续解析 false:停止解析
}
tokenValueCallback(name: xml.EventType, value: xml.ParseInfo): boolean {//只需要关心 START_TAG  END_TAG TEXT 这三个类型就好let nameStr = ''if(name == xml.EventType.START_TAG || name == xml.EventType.END_TAG || name == xml.EventType.TEXT){nameStr = this.getTagEventName(name)}else{return true}hilog.error(0x01, 'HtmlParsePage', `token    ${nameStr}  getName:${value.getName()}    getText:${value.getText()} `)return true
}getTagEventName(name: xml.EventType):string{let nameStr = ''if (name == xml.EventType.START_TAG) {nameStr = 'START_TAG'} else if (name == xml.EventType.END_TAG) {nameStr = 'END_TAG'} else if (name == xml.EventType.TEXT) {nameStr = 'TEXT'}return nameStr
}

然后我们开始解析,看看打印出来的数据


let options: xml.ParseOptions = {supportDoctype: true,ignoreNameSpace: true,attributeValueCallbackFunction: this.attributeValueCallback.bind(this),tokenValueCallbackFunction: this.tokenValueCallback.bind(this)
};
xmlPullParser.parse(options);

得到属性值

然后我们就可以看到结果

token    START_TAG  getName:span    getText: 
attribute  style color:blue;background:yellow 
token    TEXT  getName:    getText:这位姑娘有一双  
token    START_TAG  getName:span    getText: 
attribute  style color:blue;background:red 
token    TEXT  getName:    getText:蓝 
token    START_TAG  getName:span    getText: 
attribute  style color:red;background:blue 
token    TEXT  getName:    getText:色 
token    END_TAG  getName:span    getText: 
token    END_TAG  getName:span    getText: 
token    TEXT  getName:    getText:的眼睛 
token    END_TAG  getName:span    getText: 

这就好办多了,就像上面思路中说的一样:遇到START_TAG复制一个父标签属性对象,在属性回调中解析属性并设置属性,然后入栈。遇到TEXT则根据有无背景色属性添加一个ContainerSpan或者Span,遇到END_TAG则属性出栈。
对于属性解析,style属性根据;分割一下,将结果再按:分割,就得到了我们想要的属性名字(color、backgroud等)和属性值。然后我们就可以映射成抽象出来的Style类。
哦,对了,还有一点,如果颜色的属性值是redgreen这种英文名字,需要解析成对应的十六进制或者在鸿蒙中对应的Color对象。

映射

按照上面的介绍,我们在tokenValueCallback创建样式或者SpanContainerSpan。在attributeValueCallback中解析具体属性

  attributeValueCallback(name: string, value: string): boolean {let str = name + ' ' + value + ' ';hilog.error(0x01, 'HtmlParsePage', `attribute  ${str}`)if (name === 'href') {this.styleStack.peek().href = value} else if (name === 'size') {if (value.includes('px')) {let tmp = value.replace('px', '')this.styleStack.peek().fontSize = px2fp(parseInt(tmp))} else {this.styleStack.peek().fontSize = px2fp(parseInt(value))}} else if (name === 'style') {let attributes: string[] = value.split(';')attributes.forEach((attribute: string) => {let tmp: string [] = attribute.split(':')if (tmp[0] === 'color') {this.styleStack.peek().fontColor = this.getColorWithStr(tmp[1])} else if (tmp[0] === 'background') {this.styleStack.peek().backgroundColor = this.getColorWithStr(tmp[1])} else if (tmp[0] === 'font-size') {this.styleStack.peek().fontSize = px2fp(parseInt(tmp[1].replace('px', '')))}})} else if (name === 'color') {this.styleStack.peek().fontColor = this.getColorWithStr(value)}return true; // true:继续解析 false:停止解析}tokenValueCallback(name: xml.EventType, value: xml.ParseInfo): boolean {//只需要关心 START_TAG  END_TAG TEXT 这三个类型就好let nameStr = ''if(name == xml.EventType.START_TAG || name == xml.EventType.END_TAG || name == xml.EventType.TEXT){nameStr = this.getTagEventName(name)}else{return true}hilog.error(0x01, 'HtmlParsePage', `token    ${nameStr}  getName:${value.getName()}    getText:${value.getText()} `)if (name === xml.EventType.TEXT) {let vNode: VNode = new VNode()vNode.text = value.getText()vNode.style = copyStyle(this.styleStack.peek())this.rootNode.child?.push(vNode)}if (name === xml.EventType.START_TAG) {if (value.getName() === 'br') {let lineBreakSpan = new VNode()lineBreakSpan.text = '\n'this.rootNode.child?.push(lineBreakSpan)}if (value.getName() === 'font' || value.getName() === 'span' || value.getName() === 'a') {if (this.styleStack.isEmpty()) {this.styleStack.push(new Style())} else {let style = copyStyle(this.styleStack.peek())this.styleStack.push(style)}}}if (name === xml.EventType.END_TAG && (value.getName() === 'font' || value.getName() === 'span'|| value.getName() === 'a')) {this.styleStack.pop()}return true; //true:继续解析 false:停止解析}

渲染

所有的准备工作都做完了,渲染就成了最简单的一步。
根节点使用Text控件,判断子节点Sytle背景色属性,如果设置了其他值,就使用ContainerSpan(){Span()},如果没有,直接使用Span().
这里将属性写成了Extend(Span)形式

@Extend(Span)
function configSpanStyle(vNode: VNode) {.backgroundColor(vNode.style.backgroundColor).fontColor(vNode.style.href ? vNode.style.hrefFontColor : vNode.style.fontColor).fontSize(vNode.style.fontSize).onClick((event: ClickEvent) => {if (vNode.style.href) {promptAction.showToast({ message: vNode.style.href })}})
}build() {if (this.hasParse) {this.buildWithVNode(this.rootNode)} else {LoadingProgress().color(Color.Blue).width(10).height(10)}}@BuilderbuildWithVNode(vNode: VNode) {Text() {ForEach(vNode.child, (child: VNode) => {if (child.style.backgroundColor != Color.Transparent) {ContainerSpan() {Span(child.text).configSpanStyle(child)}.textBackgroundStyle({ color: child.style.backgroundColor })} else {Span(child.text).configSpanStyle(child)}})}}

到这里就结束了,源码在githubgithub、gitee

当然代码都是硬怼上去的,有很多可以改进的地方:

  1. 上面提到的默认属性,可以由调用者传入
  2. 结果标签和属性目前也是直接写死的支持哪些,其实可以做成责任链,解析可以让使用者自定义,更加方便扩展
  3. 因为是使用xml的解析器,遇到不复合标准的富文本会崩溃,要么在解析上加个try catch。或者在使用无属性的span标签再包裹一下。建议可以两个都用上,毕竟墨菲定律

下个版本再说吧


文章转载自:

http://QjliFmaV.jqmmf.cn
http://ojspfIkf.jqmmf.cn
http://PdkKm7zn.jqmmf.cn
http://GF5aFjiH.jqmmf.cn
http://o1vtCSVq.jqmmf.cn
http://LlL2nuHp.jqmmf.cn
http://a8rcLFnx.jqmmf.cn
http://472u6fD4.jqmmf.cn
http://w0QdgtnG.jqmmf.cn
http://xdmN3h35.jqmmf.cn
http://tqOBlLML.jqmmf.cn
http://RvwmMRGO.jqmmf.cn
http://pNnY1vbm.jqmmf.cn
http://7FxfQGsc.jqmmf.cn
http://613JCbvg.jqmmf.cn
http://xTw5uUni.jqmmf.cn
http://QIL3aYNf.jqmmf.cn
http://gbdTrOBl.jqmmf.cn
http://KifPxjMY.jqmmf.cn
http://GMUQsqGB.jqmmf.cn
http://cD6Nazy6.jqmmf.cn
http://iDtbjiZe.jqmmf.cn
http://YL9FkkIR.jqmmf.cn
http://G8hrma14.jqmmf.cn
http://adCbiXG8.jqmmf.cn
http://EjH6j6H8.jqmmf.cn
http://V2N9m79s.jqmmf.cn
http://a83IcGje.jqmmf.cn
http://3Q0piBJG.jqmmf.cn
http://sls0RaVP.jqmmf.cn
http://www.dtcms.com/wzjs/743781.html

相关文章:

  • 电子商务网站的建设过程漫画网站开发源码
  • 五金模具技术支持 东莞网站建设昆明网站制作的方法
  • 怎样建设网站流程网络教育
  • 2003访问网站提示输入用户名密码网页开发需要的技术
  • 网站建设策划书前言如何评价一个网页的设计
  • 普通网站 手机网站个人做跨境电商哪个平台好
  • 博客网站源码wordpress 书籍
  • 网站记录ip 修改电商网站建设教案
  • 做网站 转行wordpress怎么登陆ftp
  • 网站右侧二维码代码html小清新类型网站
  • 长安镇网站建设重庆网站推广大全
  • 网站设计有创意的主题潍坊美丽乡村建设一般发了哪个网站
  • wordpress有多少网站营销自动化系统
  • 农产品网站建设计划书iis默认网站怎么设置
  • 贵州城乡住房建设厅网站网站如何做微信支付宝支付宝支付
  • 怎么优化网站源代码成都百度推广效果
  • 现在学做网站赚钱吗网架加工多少钱一吨
  • 模板网站和定制网站后缀的区别深圳住房和建设局网站办事跟踪
  • 钢管网站模板网站设计公司杭州
  • 潍坊手机网站网站开发需要的技术
  • 女与男爱做电影网站免费下载重庆网站页设计制作
  • 个人搭建网站要多少钱内容营销案例
  • 建设银行深圳天健世纪支行网站合肥工大建设监理有限公司网站
  • 烟台城乡建设局官方信息网站网页设计实训总结三百字
  • 网站建设外包公司招聘软件项目报价
  • 顺的品牌网站设计信息网站设计分析报告
  • 重庆放心seo整站优化做网站如何找广告商
  • 信息课做网站的软件关于网站建设培训
  • 怎么写公司网站的文案台州做企业网站
  • 富阳网站建设怎样德语网站建设注意事项