Android: This requirement is confusing, the product says to implement rich text echo display

I. Introduction

Isn’t it just displaying a rich text? What’s the difficulty? As for making such a fuss, hey, old guys, don’t panic, first look at the requirements, and then let’s find out.

1. General demand

Requirements, the user content editing page, realize the mixed arrangement of pictures and texts, 1. The picture can be inserted at any position, and can be sorted by long pressing and dragging; 2. After the picture is dragged, if there is no content above and below, the input position needs to be vacant, and there is content. 3. The content supports insertion at any position; 4. Pass it to the background in the form of rich text; 5. Parse the rich text and echo the content.

2. Approximate rendering

It is not difficult to realize this requirement. Just a RecyclerView is enough. It is nothing more than using ItemTouchHelper, and after binding with RecyclerView, the position conversion of Item is realized in the onMove method. Of course, some logic between pictures and input boxes needs to be processed. This is not the focus of this article, and I will talk about it later.

For the effect, I wrote a Demo separately, which is the same as the one used in the project. The specific effect is as follows:

The way to obtain rich text is also relatively simple. Regardless of the text or the picture, it is finally stored in the collection. We directly traverse the collection and set the corresponding rich text tags for the pictures and text. Specific attributes, such as width and height, color The size can be defined by yourself, roughly as follows:

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

The above-mentioned links, anyway, are relatively smooth, and then we come to our topic today. We have uploaded the rich text, but how to echo it?

2. Rich text echo analysis

There are two situations for echoing. The first one is that after editing, it can be saved to a draft and needs to be echoed when editing next time. The second situation is that the content has been published and can be edited again.

There are many ways to echo specific drafts. Didn’t we use RecyclerView to implement it? Just save the list data directly. You can use local or database storage methods. No matter which one you use, it is not difficult to implement. Echo Sometimes it can also be passed to RecyclerView in the form of a collection.

The content has already been published. This is the focus of the research. Since the interface returns rich text information, what I thought at first was that the rich text information has to parse the content inside, which is really troublesome. I think every time the release is successful Afterwards, store a copy of the data locally, and when editing, search for the stored data according to the agreed logo, which is indeed possible, but ignore that this is network data, and the device can be replaced. If you change the device, where to get the data from? Woolen cloth? Haha, this kind of opportunistic plan is really not advisable.

Then there is no way, just parse the rich text, then take out the pictures and content one by one, package them into a collection, and echo them to the RecyclerView.

3. Rich text analysis

The following is the rich text information of a certain field after the release is successful. After we get it, we need to echo it to the edit page, that is, the custom RecyclerView. Old irons, what is your first solution?

<p>我是测试内容</p><p>我是测试内容12333</p><img src="https://www.vipandroid.cn/ming/image/gan.png"/><p>我是测试内容88888</p><p>我是测试内容99999999</p><img src="https://www.vipandroid.cn/ming/image/zao.png"/>

The data we finally need to get is as follows. Only in this way can we encapsulate them into the collection one by one and echo them into the list.

我是测试内容
我是测试内容12333
https://www.vipandroid.cn/ming/image/gan.png
我是测试内容88888
我是测试内容99999999
https://www.vipandroid.cn/ming/image/zao.png

String interception, I believe this is everyone's first instinct, how to intercept to get the content in the label? I can tell you responsibly that interception is achievable, and there is a lot of logic to be implemented. Let me simply give an example of interception:

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

There are many ways to intercept, but no matter which one, your judgment is indispensable. In order to obtain the corresponding content, you have to nest in multiple levels, and you have to intercept again and again. Although it is realized, but It makes code redundant and loses efficiency. Currently, there are only two tags. What if there are multiple tags for rich text in the future? It's scary to think about it.

Is there a simpler way? There must be, that is, regular expressions. Two problems need to be solved. First, how to use regular expressions? Second, how to write regular expressions? After understanding these two items, it is very simple to obtain the desired content in the rich text.

4. Regular use in Kotlin

Speaking of regularity, we have to talk about regularity in Java. This is something we are very familiar with when doing Android, and it is generally the most commonly used. The basic code is as follows:

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

To get the matching content, just take the corresponding group. There are too many examples, so I won’t mention it separately. In addition to the APIs provided in Java, Kotlin also provides related APIs, which are extremely simple to use. .

In Kotlin, we can use the Regex object, which is mainly used to search strings or replace regular expression objects. Let's give a few simple examples.

1. Determine whether a certain string is contained, containsMatchIn

 val regex = Regex("Ming")//定义匹配规则
 val matched = regex.containsMatchIn("AbnerMing")//传入内容
 print(matched)

output result

true

2. Match the target string matches

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

output result

true
false

3. Return the first occurrence of the specified string find

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

output result

2023-6-28

4. Return all occurrences of the target string 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)
        }

output result

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

ok, of course, there are many methods in it, such as replacement, segmentation, etc., which will not be introduced here. I will add a follow-up article when I have time. Basically, the above methods are commonly used.

5. Rich text uses regular expressions to obtain content

There are many tags in a rich text, obviously we all need to get the contents inside, here we must use the findAll method, but how do we set the regular expressions of the tags?

We know that tags in rich text are composed of left and right angle brackets, such as <p></p>, <a></a>, and of course there are single tags, such as <img/>, <br/> Wait, then there is a rule, it is nothing more than the beginning <beginning, then an uncertain letter, and the ending >.

1. Label exact match

For example, if there is such a rich text, we need to get all the <p></p> tags.

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

Our regular expression is as follows:

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

What does it mean? It starts with <p and ends with </p>. The dot. is all characters except the newline character. * means 0 or more matches, and ? means 0 or 1 match. The reason why it starts like this An important reason for writing <p.*?> instead of <p> is that attributes or spaces need to be matched. Otherwise, rich text with attributes or spaces cannot be matched. This needs attention!

basic code

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

operation result

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

Seeing the above results, some veterans asked, what I want is the content, how to return the tags, this seems a bit wrong, if we only need to match the string, it is right now, But if you want the content in the label, then our regularization needs to be optimized again. How to optimize it is to add a start and end position. The start position of the content is "<" the end position is ">", as shown below

We just need to change the starting position:

matching content

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

operation result

我是一个段落
我是另一个一个段落

2. Match all tags

With the exact label matching, it becomes very simple to match all the label content in the rich text. It is nothing more than replacing the p in the above case with an uncertain letter.

matching content

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

operation result

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

3. Single label matching

It seems to have met our needs, because the content in the rich text has been obtained, encapsulated into a collection, and passed to the list. However, the above regularity seems to be only for double tags, and it cannot be satisfied with a single tag. For example, let’s look at the rich text we want to match at the beginning. The above regex cannot match the src content in the img tag. What should we do?

<p>我是测试内容</p><p>我是测试内容12333</p><img src="https://www.vipandroid.cn/ming/image/gan.png"/><p>我是测试内容88888</p><p>我是测试内容99999999</p><img src="https://www.vipandroid.cn/ming/image/zao.png"/>

It’s very simple, single tag is processed separately, what else can be done, multiple regular expressions can be spliced ​​with or, and the attribute value is also obtained in this way, positioning the start and end positions, such as the above img tag, if you want to get it The content in src only needs to locate the start position "src="" and the end position """.

matching content

val content =
            "<p>我是测试内容</p><p>我是测试内容12333</p><img src=\"https://www.vipandroid.cn/ming/image/gan.png\"/><p>我是测试内容88888</p><p>我是测试内容99999999</p><img src=\"https://www.vipandroid.cn/ming/image/zao.png\"/>"
        val matchResult =
            Regex("""((?<=<[A-Za-z]*>).+?(?=</[A-Za-z]*>))|((?<=src=").+?(?="))""").findAll(content)
        matchResult.forEach {
            println(it.value)
        }

operation result

我是测试内容
我是测试内容12333
https://www.vipandroid.cn/ming/image/gan.png
我是测试内容88888
我是测试内容99999999
https://www.vipandroid.cn/ming/image/zao.png

This is not the end, it is simple, the data you are thinking about is obtained, after obtaining the content of the rich text label, then package it into a collection, and echo it to RcyclerView, isn’t it easy, haha~

Click Draft, the effect is as follows:

6. Summary

Under the positive interception thinking, regular expressions are undoubtedly the simplest. Rich text, whether it is label matching or content and attributes, can use regular expressions for simple matching, and it can be done easily. It should be noted that, The matching rules for different attributes are different and need to be analyzed according to specific situations.

Guess you like

Origin blog.csdn.net/ming_147/article/details/131441506