一、前语

不就展示一个富文本吗,有啥难的,至于这么大惊小怪吗,哎,各位老铁,莫慌,先看需求,咱们再一探究竟。

1、大致需求

要求,用户内容修改页面,完成图文混排,1、图片随意方位刺进,并且可长按拖动排序;2、图片拖动完成之后,上下无内容,则需求空出输入方位,有内容,则无需空出;3、内容支撑随意方位刺进;4、以富文本的办法传入后台;5、解析富文本,回显内容。

2、大致作用图

Android:这个需求搞懵了,产品说要实现富文本回显展示

完成这个需求倒不是很难,直接一个RecyclerView就搞定了,无非便是运用ItemTouchHelper,再和RecyclerView绑定之后,在onMove办法里完成Item的方位转化,当然需求处理一些图片和输入框之间的逻辑,这个不是本篇文章的要点,今后再说一块。

作用的话,我又独自的写了一个Demo,和项目中用到的相同,具体作用如下:

获取富文本的办法也是比较的简略,不论文本仍是图片,终究都是存到调集中,咱们直接遍历调集,给图片和文字设置对应的富文本标签即可,具体的特点,比方宽高,颜色大小,能够自行界说,大致如下:

/**
     * AUTHOR:AbnerMing
     * INTRODUCE:返回富文本数据
     */
    fun getRichContent(): String {
        val endContent = StringBuffer()
        mRichList.forEach {
            if (it.isPic == 0) {
                //图片
                endContent.append("<img src="https://juejin.im/post/7249604020875984955/" + it.image + "https://juejin.im/post/7249604020875984955/"/>")
            } else {
                //文本
                endContent.append("<p>" + it.text + "</p>")
            }
        }
        return endContent.toString()
    }

以上的各个环节,不论怎样说,仍是比较的顺利,接下来就到了咱们今天的话题了,富文本咱们是传上去了,可是怎样回显呢?

二、富文本回显剖析

回显有两种状况,榜首种是修改之后,能够保存至草稿,下次再修改时,需求回显;第二种状况是,内容现已发布了,能够再次修改内容。

具体的草稿回显有多种办法,咱们不是运用RecyclerView完成的吗,直接保存列表数据就能够了,能够运用本地或许数据库办法的存储办法,不论运用哪种,完成起来绝非难事,回显的时分也是以调集的办法传入RecyclerView即可。

内容现已发布过的,这才是探究的要点,由于接口返回的是富文本信息,一开端无脑想到的是,富文本信息还得要解析里边的内容,着实麻烦,想着每次发布成功之后在本地存储一份数据,在修改的时分,依据约定好的标识去存储的数据里找,的确能够完成,可是疏忽了这是网络数据,是能够更换设备的,换个设备,数据从哪取呢?哈哈,这种投机取巧的计划,实在是不可取。

那没办法了,解析富文本呗,然后逐次取出图片和内容,再封装成调集,回显到RecyclerView中即可。

三、富文本解析

以下是发布成功后,某个字段的富文本信息,咱们拿到之后,需求回显到修改的页面,也便是自界说的RecyclerView中,老铁们,你们的榜首处理计划是什么?

<p>我是测验内容</p><p>我是测验内容12333</p><img src="https://www.6hu.cc/files/2023/07/1688880879-7ac3c7b5ce71919.png"/><p>我是测验内容88888</p><p>我是测验内容99999999</p><img src="https://www.6hu.cc/files/2023/07/1688880873-46016259ac5b247.png"/>

咱们终究需求拿到的数据,如下,只有这样,咱们才干逐个封装到调集,回显到列表中。

    我是测验内容
    我是测验内容12333
    https://www.6hu.cc/files/2023/07/1688880879-7ac3c7b5ce71919.png
    我是测验内容88888
    我是测验内容99999999
    https://www.6hu.cc/files/2023/07/1688880873-46016259ac5b247.png

字符串截取呗,我相信这是咱们的榜首直觉,以什么办法截取,才干拿到标签里的内容呢?能够负责任的告知咱们,截取是能够完成的,需求完成的逻辑有点多,我简略的举一个截取的比方:

            content = content.replace("<p>", "")
            val split = content.split("</p>")
            val contentList = arrayListOf<String>()
            for (i in split.indices) {
                val pContent = split[i]
                if (TextUtils.isEmpty(pContent)) {
                    continue
                }
                if (pContent.contains("img")) {
                    //包含了图片
                    val imgContent = pContent.split("/>")
                    for (j in imgContent.indices) {
                        val img = imgContent[j]
                        if (img.contains("img")) {
                            //图片,需求再次截取
                            val index = img.indexOf(""")
                            val last = img.lastIndexOf(""")
                            val endImg = img.substring(index + 1, last)//终究的图片内容
                            contentList.add(endImg)
                        } else {
                            //文本内容
                            contentList.add(img)
                        }
                    }
                } else {
                    contentList.add(pContent)
                }
            }

截取的办法有很多种,可是不论哪一种,你的判断是少不了的,为了取得对应的内容,不得不多级嵌套,不得不一而再再而三的进行截取,尽管完成了,可是其冗余了代码,丢掉了效率,现在仍是仅有两种标签,如果说今后的富文本有多种标签呢?想想都可怕。

有没有一种比较简洁的办法呢?必须有,那便是正则表达式,需求处理两个问题,榜首、正则怎样用?第二,正则表达式怎样写?搞理解这两条之后,获取富文本中想要的内容就很简略了。

四、Kotlin中的正则运用

提到正则,咱就不得不聊聊Java中的正则,这是咱们做Android再熟悉不过的,一般也是最常用的,基本代码如下:

    String str = "";//匹配内容
    String pattern = "";//正则表达式
    Pattern r = Pattern.compile(pattern);
    Matcher m = r.matcher(str);
    System.out.println(m.matches());

获取匹配内容的话,取对应的group即可,这个比方太多了,就不独自举了,除了Java中供给的Api之外,在Kotlin傍边,也供给了相关的Api,运用起来也是无比的简略。

在Kotlin中,咱们能够运用Regex这个方针,主要用于搜索字符串或替换正则表达式方针,咱们举几个简略的比方。

1、判定是否包含某个字符串,containsMatchIn

     val regex = Regex("Ming")//界说匹配规矩
     val matched = regex.containsMatchIn("AbnerMing")//传入内容
     print(matched)

输出成果

    true

2、匹配方针字符串matches

     val regex = """[A-Za-z]+""".toRegex()//只匹配英文字母
     val matches1 = regex.matches("abcdABCD")
     val matches2 = regex.matches("12345678")
     println(matches1)
     println(matches2)

输出成果

    true
    false

3、返回初次呈现指定字符串find

    val time = Regex("""\d{4}-\d{1,2}-\d{1,2}""")
    val timeValue= time.find("今天是2023-6-28,北京,有雨,请记得带雨伞!")?.value
    println(timeValue)

输出成果

    2023-6-28

4、返回一切状况呈现方针字符串findAll

     val time = Regex("""\d{4}-\d{1,2}-\d{1,2}""")
            val timeValue = time.findAll(
                "今天是2023-6-28,北京,有雨,请记得带雨伞!" +
                        "明天是2023-6-29,或许就没有雨了,具体得等到后天2023-6-30日才干知晓!"
            )
            timeValue.forEach {
                println(it.value)
            }

输出成果

    2023-6-28
    2023-6-29
    2023-6-30

ok,当然了,里边还有许多办法,比方替换,切割等,这儿就不介绍了,后续有时间补一篇,基本上常用的便是以上的几个办法。

五、富文本运用正则获取内容

一个富文本里的标签有很多个,显然咱们都需求进行获取里边的内容,这儿肯定是要运用findAll这个办法了,可是,咱们该怎样设置标签的正则表达式呢?

咱们知道,富文本中的标签,都是有左右尖括号组成的,比方<p></p>,<a></a>,当然也有单标签,比方<img/>,<br/>等,那这就有规律了,无非便是最初<开端,然后是不确定字母,再加上完毕的>就能够了。

1、标签精确匹配

比方有这样一个富文本,咱们要获取一切的<p></p>标签。

 <div>早上好啊</div><p>我是一个阶段</p><a>我是一个链接</a><p>我是另一个一个阶段</p>

咱们的正则表达式就如下:

  <p.*?>(.*?)</p>

什么意思呢,便是以<p最初,</p>完毕,这个点.是除换行符以外的一切字符,*为匹配0次或多次,?为0次或1次匹配,之所以最初这样写<p.*?>而不是<p>,一个重要的原因便是需求匹配到特点或许空格,要不然富文本中带了特点或空格,就无法匹配了,这个需求留意!

基本代码

         val content = "<div>早上好啊</div><p>我是一个阶段</p><a>我是一个链接</a><p>我是另一个一个阶段</p>"
            val matchResult = Regex("""<p.*?>(.*?)</p>""").findAll(content)
            matchResult.forEach {
                println(it.value)
            }

运转成果

   <p>我是一个阶段</p>
   <p>我是另一个一个阶段</p>

看到上面的的成果,有的老铁就问了,我要的是内容啊,怎样把标签也返回了,这如同有点不对吧,如果说咱们只需匹配到的字符串,现在是对的,可是想要标签里的内容,那么咱们的正则需求再优化一下,怎样优化呢,便是添加一个开端和完毕的方位,内容的开端方位是”<“完毕方位是”>“,如下图

Android:这个需求搞懵了,产品说要实现富文本回显展示

咱们只需求更改下起始方位即可:

匹配内容

     val content = "<div>早上好啊</div><p>我是一个阶段</p><a>我是一个链接</a><p>我是另一个一个阶段</p>"
     val matchResult = Regex("""(?<=<p>).*?(?=</p>)""").findAll(content)
     matchResult.forEach {
                println(it.value)
            }

运转成果

    我是一个阶段
    我是另一个一个阶段

2、一切标签进行匹配

有了标签精确匹配之后,针对富文本里的一切的标签内容匹配,就变得很是简略了,无非便是要把上边案例中的p换成一个不确定字母即可。

匹配内容

     val content = "<div>早上好啊</div><p>我是一个阶段</p><a>我是一个链接</a><p>我是另一个一个阶段</p>"
     val matchResult = Regex("""(?<=<[A-Za-z]*>).+?(?=</[A-Za-z]*>)""").findAll(content)
     matchResult.forEach {
                println(it.value)
}

运转成果

    早上好啊
    我是一个阶段
    我是一个链接
    我是另一个一个阶段

3、单标签匹配

好像现已满足咱们的需求了,由于富文本中的内容现已拿到了,封装到调集之中,传递到列表中即可,可是,以上的正则好像只针对双标签的,带有单标签就无法满足了,比方,咱们再看下初始咱们要匹配的富文本,以上的正则是匹配不到img标签里的src内容的,怎样搞?

 <p>我是测验内容</p><p>我是测验内容12333</p><img src="https://www.6hu.cc/files/2023/07/1688880879-7ac3c7b5ce71919.png"/><p>我是测验内容88888</p><p>我是测验内容99999999</p><img src="https://www.6hu.cc/files/2023/07/1688880873-46016259ac5b247.png"/>

很简略,单标签独自处理呗,还能咋弄,多个正则表达式,用或拼接即可,特点值也是这样的获取原则,定位开端和完毕方位,比方以上的img标签,如果要获取到src中的内容,只需求定位开端方位”src=”https://juejin.im/post/7249604020875984955/%E2%80%9C%EF%BC%8C%E5%92%8C%E7%BB%93%E6%9D%9F%E4%BD%8D%E7%BD%AE%E2%80%9D”“即可。

匹配内容

    val content =
                "<p>我是测验内容</p><p>我是测验内容12333</p><img src="https://juejin.im/post/7249604020875984955/https://www.vipandroid.cn/ming/image/gan.png"/><p>我是测验内容88888</p><p>我是测验内容99999999</p><img src="https://www.6hu.cc/files/2023/07/1688880873-46016259ac5b247.png"/>"
            val matchResult =
                Regex("""((?<=<[A-Za-z]*>).+?(?=</[A-Za-z]*>))|((?<=src="https://juejin.im/post/7249604020875984955/).+?(?="))""").findAll(content)
            matchResult.forEach {
                println(it.value)
            }

运转成果

    我是测验内容
    我是测验内容12333
    https://www.6hu.cc/files/2023/07/1688880879-7ac3c7b5ce71919.png
    我是测验内容88888
    我是测验内容99999999
    https://www.6hu.cc/files/2023/07/1688880873-46016259ac5b247.png

这不就完事了,简简略单,心心念念的数据就拿到了,拿到富文本标签内容之后,再封装成调集,回显到RcyclerView中就能够了,这不很easy吗,哈哈~

点击草稿,咱们看下作用:

Android:这个需求搞懵了,产品说要实现富文本回显展示

六、总结

在正向的截取思想下,正则表达式无疑是最简略的,富文本,不论是标签匹配仍是内容以及特点,都能够运用正则进行简略的匹配,轻轻松松就能搞定,需求留意的是,不同特点的匹配规矩是不相同的,需求依据特有的状况去剖析。