This article has been authorized to be reprinted by guolin_blog, an official wechat account.

The control makes it easy to debug the response data, as shown below:

Control styles refer to the following website:

JSON online parsing and formatting verification

Project GitHub: JSONRecyclerView

Project Demo GitHub: JSONRecyclerViewDemo

An overview of the

Control is based on RecyclerView, the text will present the corresponding color to reflect the corresponding type, if the value type is JSONObject or JSONArray, the corresponding view of the data can be expanded or contracted, in addition, the size or color of the text can be customized change.

Texts are divided into the following seven types:

  • Common text: colon, curly braces, curly braces, comma, Object{… } and Array [].
  • Text of type key
  • String text
  • Text of type Number
  • Boolean text
  • The url text
  • Null text

The default text color is as follows:

<color name="default_text_color"># 333333</color>
<color name="default_key_color">#92278f</color>
<color name="default_string_color">#3ab54a</color>
<color name="default_number_color">#25aae2</color>
<color name="default_boolean_color">#f98280</color>
<color name="default_url_color">#61d2d6</color>
<color name="default_null_color">#f1592a</color>
Copy the code

Method of use

The usage method is as follows:

Import to project

Add the following code to the build.gradle file of the main project:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io'}}}Copy the code

Then add the dependency to the module you want to use. The corresponding build.gradle file code looks like this:

dependencies {
    implementation 'com. Making. TanJiaJunBeyond: JSONRecyclerView: 1.0.0'
}
Copy the code

Add to view

Then add JsonRecyclerView to the XML file as follows:

<com.tanjiajun.jsonrecyclerview.view.JSONRecyclerView
    android:id="@+id/rv_json"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Copy the code

Or add it to the view using the addView related method as follows:

val rvJson = JSONRecyclerView(this)
linearLayout.addView(rvJson)
Copy the code

We can change the style using the setStyle method as follows:

rvJson.setStyles(textColor = ContextCompat.getColor(this,R.color.black))
Copy the code

Data binding

BindData using the bindData method, which accepts JSON string, JSONObject, and JSONArray data.

When our data type is JSON string, we can call the following methods:

// JSONRecyclerView.kt
/** * Bind JSON string data. * Bind the json string data. * *@paramJsonString The JSON string to bind. * /
fun bindData(jsonString: String) =
        adapter.bindData(jsonString)
Copy the code

When our data type is JSONObject, we can call the following methods:

// JSONRecyclerView.kt
/** * bind JSONObject data. * Bind the json object data. * *@paramJsonObject The JSON object to bind. * /
fun bindData(jsonObject: JSONObject) =
        adapter.bindData(jsonObject)	
Copy the code

When our data type is JSONArray, we can call the following methods:

// JSONRecyclerView.kt
/** * Bind JSONArray data. * Bind the json array data. * *@paramJsonArray The JSON array to bind. * /
fun bindData(jsonArray: JSONArray) =
        adapter.bindData(jsonArray)
Copy the code

The sample code

The code looks like this:

package com.tanjiajun.jsonrecyclerviewdemo

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.tanjiajun.jsonrecyclerview.view.JSONRecyclerView

/** * Created by TanJiaJun on 6/1/21. */
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<JSONRecyclerView>(R.id.rv_json).bindData(
            "{\n" +
                    " \"string\":\"string\",\n" +
                    " \"number\":100,\n" +
                    " \"boolean\":true,\n" +
                    " \"url\":\"https://github.com/TanJiaJunBeyond/JSONRecyclerView\",\n" +
                    " \"JSONObject\":{\n" +
                    " \"string\":\"string\",\n" +
                    " \"number\":100,\n" +
                    " \"boolean\":true\n" +
                    " },\n" +
                    " \"JSONArray\":[\n" +
                    " {\n" +
                    " \"string\":\"string\",\n" +
                    " \"number\":100,\n" +
                    " \"boolean\":true\n" +
                    " }\n" +
                    " ]\n" +
                    "}")}}Copy the code

The core code

Most of the codes have corresponding annotations in Both Chinese and English, maybe some translation is not good, please forgive me.

As mentioned above, the control is based onRecyclerView, involving JsonItemView, JsonViewAdapter and JsonRecyclerView three classes.

JSONItemView

The layout file is Item_jSON_view.xml. This class inherits the LinearLayout with four key variables, as shown below:

// JSONItemView.kt
private lateinit var tvLeft: TextView
private lateinit var ivIcon: ImageView
private lateinit var tvRight: TextView

/** * Set the scaled Pixel text size. * /
var textSize = DEFAULT_TEXT_SIZE_SP
    set(value) {
        // Range is [12.0f, 30.0f]
        field = when {
            value < 12.0 F -> 12.0 F
            value > 30.0 F -> 30.0 F
            else -> value
        }
        // Set the text size of the text on the left
        tvLeft.textSize = field
        // Set the size of the display expand and shrink ICONS
        val size = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            field,
            resources.displayMetrics
        ).toInt()
        ivIcon.layoutParams = (ivIcon.layoutParams as LinearLayout.LayoutParams).apply {
            width = size
            height = size
        }
        // Set the text size of the text on the right
        tvRight.textSize = field
    }
Copy the code
  • The variable tvLeft is the TextView on the left that displays the key-related text.

  • The variable ivIcon is the ImageView in the middle, used to display the expanded or contracted icon

  • The variable tvRight is the TextView on the right that displays the text associated with value.

  • TextSize is a public variable that can be used to change the size of the text. Note that the unit is sp. The minimum value of the text is 12sp and the maximum value is 30sp.

JSONViewAdapter

This class is used to process different types of data and click related logic, it inherits RecyclerView.Adapter, mainly involves the following key methods:

OnBindViewHolder method

The code looks like this:

// JSONViewAdapter.kt
override fun onBindViewHolder(holder: JSONViewAdapter.JsonItemViewHolder, position: Int) {
    with(holder.jsonItemView) {
        textSize = this@JSONViewAdapter.textSize setRightColor(textColor) jsonObject? .let { bindJSONObjectData(position, it) } jsonArray? .let { bindJSONArrayData(position, it) } } }Copy the code

It binds data to the view, calling the bindJSONObjectData method if the data type is JSONObject or the bindJSONArrayData method if the data type is JSONArray.

HandleValue method

The code looks like this:

// JSONViewAdapter.kt
/** * Handle the styling of the right part of the json item view (i.e., The part that shows the value). * Handles the style of the right part of the JsonItemView (that is, the part that shows the value). * *@paramImage The image to be displayed in The JSON item view. *@paramThe json Item view to be processed. *@paramAppendComma Whether to append commas. *@paramHierarchy The number of views hierarchies. * /
private fun handleValue(
    value: Any? , itemView:JSONItemView,
    appendComma: Boolean,
    hierarchy: Int
) {
    itemView.showRight(SpannableStringBuilder().apply {
        when (value) {
            is Number ->
                // Handle styles with values of type Number
                handleNumberValue(itemView, value)
            is Boolean ->
                // Handle styles with Boolean values
                handleBooleanValue(itemView, value)
            is String ->
                // Handle styles with a String value
                handleStringValue(itemView, value)
            is JSONObject ->
                // Handle styles with values of type JSONObject
                handleJSONObjectValue(itemView, value, appendComma, hierarchy)
            is JSONArray ->
                // Handle styles with values of type JSONArray
                handleJSONArrayValue(itemView, value, appendComma, hierarchy)
            else ->
                // Handle the null style
                handleNullValue(itemView)
        }
        if (appendComma) append(",")})}Copy the code

OnClick method

If the data type is JSONObject or JSONArray, you can click to expand or shrink the view as follows:

The performFirstExpand method is called if it is the first expansion, otherwise the performClick method is called as follows:

// JSONViewAdapter.kt
override fun onClick(v: View?). {
    // If itemView has 1 child views, this is the first expansion
    (itemView.childCount == 1)
        .yes { performFirstExpand() }
        .otherwise { performClick() }
}
Copy the code

PerformFirstExpand method

This method is used to expand the corresponding itemView of JSONObject or JSONArray for the first time. The code is as follows:

// JSONViewAdapter.kt
/** * The first time the view corresponding to a JSONObject or JSONArray is expanded. * Expand the itemView corresponding to the JSONObject or JSONArray for the first time. * /
private fun performFirstExpand(a) {
    isExpanded = true
    itemView.showIcon(false)
    itemView.tag = itemView.getRightText()
    itemView.showRight(if (isJsonObject) "{" else "[")

    // Expand the view below that level
    val array: JSONArray? =
        if (isJsonObject) (value as JSONObject).names() else value as JSONArray
    vallength = array? .length() ? :0
    for (i in 0 until length) {
        itemView.addViewNoInvalidate(JSONItemView(itemView.context).apply {
            textSize = this@JSONViewAdapter.textSize
            setRightColor(textColor)
            valchildValue = array? .opt(i) isJsonObject .yes { handleJSONObject( key = childValueas String,
                        value = (value as JSONObject)[childValue],
                        appendComma = i < length - 1,
                        hierarchy = hierarchy
                    )
                }
                .otherwise {
                    handleJSONArray(
                        value = childValue,
                        appendComma = i < length - 1,
                        hierarchy = hierarchy
                    )
                }
        })
    }
    // Displays the last view of the hierarchy
    itemView.addViewNoInvalidate(JSONItemView(itemView.context).apply {
        textSize = this@JSONViewAdapter.textSize
        setRightColor(textColor)
        showRight(
            StringBuilder(getHierarchyStr(hierarchy - 1))
                .append(if (isJsonObject) "}" else "]")
                .append(if (appendComma) "," else ""))})/ / redraw itemView
    itemView.requestLayout()
    itemView.invalidate()
}
Copy the code

PerformClick method

This method is used to expand or shrink when clicked, as shown below:

/** * Click to expand or collapse. * /
private fun performClick(a) {
    itemView.showIcon(isExpanded)
    val rightText = itemView.getRightText()
    itemView.showRight(itemView.tag as CharSequence)
    itemView.tag = rightText
    for (i in 1 until itemView.childCount) {
        // Make all child views visible if expanded, hidden otherwise
        itemView.getChildAt(i).visibility = if (isExpanded) View.GONE elseView.VISIBLE } isExpanded = ! isExpanded }Copy the code

If the data type is url, you can click to open the browser and view it as follows:

/** * Click to expand or collapse. * /
private fun performClick(a) {
    itemView.showIcon(isExpanded)
    val rightText = itemView.getRightText()
    itemView.showRight(itemView.tag as CharSequence)
    itemView.tag = rightText
    for (i in 1 until itemView.childCount) {
        // Make all child views visible if expanded, hidden otherwise
        itemView.getChildAt(i).visibility = if (isExpanded) View.GONE elseView.VISIBLE } isExpanded = ! isExpanded }Copy the code

The regular expression to determine whether it is a URL type is as follows, and the comments are quite detailed, so I won’t repeat them here:

private val urlPattern: Pattern = Pattern.compile(
    / / verification is http://, https://, ftp://, RTSP: / /, mms://, one of them
    "^((http|https|ftp|rtsp|mms)? : / /)?" +
            // Check whether the character is an FTP address (ftp://user:password@)
            // Check whether the characters are 0 to 9, lowercase letters a to z, _,! , ~, *, ', (,), period (.), &, =, +, $, %, - One of them can be matched zero times or once
            "(([0-9a-z_!~*'().&=+\$%-]+: )?" +
            // Check whether the characters are 0 to 9, lowercase letters a to z, _,! , ~, *, ', (,), period (.), &, =, +, $, %, - One of them can be matched once or more
            "[0-9a-z_!~*'().&=+\$%-]+" +
            / / @
            "@)?" +
            // Check whether the character is an IP address, for example, 192.168.255.255
            // Determine if the character matches 1+[0 to 9, twice], for example, 192
            "((1\\d{2}" +
            / / or
            "|" +
            // Check whether the character matches 2+[0 to 4, once]+[0 to 9, once], for example, 225
            "2[0-4]\\d" +
            / / or
            "|" +
            // Determine if the character matches 25+[0 to 5, once], for example, 255
            "25 [0 to 5]" +
            / / or
            "|" +
            // Check whether the character matches [1 to 9, once]+[0 to 9, once], for example, 25
            "[1-9]\\d" +
            / / or
            "|" +
            // Determine if the character matches 1 to 9 once, for example, 5
            "[1-9])" +
            Matching \. / / whether characters (1 \ d {2} | \ [0-4] 2 d 25 [0 to 5) | | \ | d \ [1-9] d), matching three times
            "(\\.(" +
            // Determine if the character matches 1+[0 to 9, twice], for example, 192
            "1\\d{2}" +
            / / or
            "|" +
            // Check whether the character matches 2+[0 to 4, once]+[0 to 9, once], for example, 225
            "2[0-4]\\d" +
            / / or
            "|" +
            // Determine if the character matches 25+[0 to 5, once], for example, 255
            "25 [0 to 5]" +
            / / or
            "|" +
            // Check whether characters match [1 to 9]+[0 to 9], for example, 25
            "[1-9]\\d" +
            / / or
            "|" +
            // Determine if the character matches 0 to 9 once, for example, 5
            "\\d))" +
            // Match three times
            "{3}" +
            / / or
            "|" +
            // Check whether the character is a Domain Name.
            // Level 3 domain name or higher, check whether the characters are 0 to 9, lowercase letters a to Z, _, and! , ~, *, ', (,), - One of them matches zero or more times, and then adds., for example, WWW.
            "([0-9a-z_!~*'()-]+\\.) *" +
            The second character is 0 to 9, lowercase letters A to Z, and -. The second character is 0 to 9, lowercase letters A to Z, and -. The second character can be matched at most 61 times. Finally determine if the third character is one of the lowercase letters a to Z and 0 to 9.
            "([0-9 a-z] [0-9 a-z -], 21 {0})? [0-9a-z]" +
            // The top-level domain name is matched at least twice and at most six times, for example,.com and.cn
            "\ \. [a-z] {2, 6})" +
            +[0 to 9, at least once and at most four times], zero times or once
            "(: [0-9] {1, 4})?" +
            // Determines whether the character is a slash (/), matching zero times or once. If there is no filename, no slash is required
            "((/?)|" +
            // Check whether the characters are 0 to 9, lowercase letters a to z, uppercase letters a to z, _,! , ~, *, ', (,),. ,? , :, @, &, =, +, $,, %, #, - One of them can be matched once or more
            "(/[0-9a-zA-Z_!~*'(){}.;?:@&=+\$,%#-]+)+" +
            // Check whether the character is a slash (/), matching zero times or once
            "/? \ $"
)
Copy the code

The regular expression visualization is as follows:

JSONRecyclerView

This class is used to present the data to the view as a list, annotated so clearly that I won’t repeat it here. The code looks like this:

// JSONRecyclerView.kt
package com.tanjiajun.jsonrecyclerview.view

import android.content.Context
import android.util.AttributeSet
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tanjiajun.jsonrecyclerview.DEFAULT_TEXT_SIZE_SP
import com.tanjiajun.jsonrecyclerview.R
import com.tanjiajun.jsonrecyclerview.adapter.JSONViewAdapter
import org.json.JSONArray
import org.json.JSONObject

/** * Created by TanJiaJun on 5/31/21. */
class JSONRecyclerView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {

    private val adapter = JSONViewAdapter(context)

    init {
        layoutManager = LinearLayoutManager(context)
        setAdapter(adapter)
    }

    /** * Bind JSON string data. * Bind the json string data. * *@paramJsonString The JSON string to bind. * /
    fun bindData(jsonString: String) =
        adapter.bindData(jsonString)

    /** * bind JSONObject data. * Bind the json object data. * *@paramJsonObject The JSON object to bind. * /
    fun bindData(jsonObject: JSONObject) =
        adapter.bindData(jsonObject)

    /** * Bind JSONArray data. * Bind the json array data. * *@paramJsonArray The JSON array to bind. * /
    fun bindData(jsonArray: JSONArray) =
        adapter.bindData(jsonArray)

    /** * Sets the style of JsonItemView. * Set the json item view styles. * *@paramTextSize The size of all text. *@paramTextColor The normal text color. (Normal text color) *@paramKeyColor The color of The text of type key. *@paramStringColor The color of The text of type String. *@paramNumberColor The color of The text of type Number. *@paramBooleanColor The color of text of type Boolean. *@paramUrlColor The color of The URL text. *@paramNullColor The color of null text. * /
    @JvmOverloads
    fun setStyles(
        textSize: Float = DEFAULT_TEXT_SIZE_SP,
        @ColorInt textColor: Int = ContextCompat.getColor(context, R.color.default_text_color).@ColorInt keyColor: Int = ContextCompat.getColor(context, R.color.default_key_color),
        @ColorInt stringColor: Int = ContextCompat.getColor(context, R.color.default_string_color),
        @ColorInt numberColor: Int = ContextCompat.getColor(context, R.color.default_number_color),
        @ColorInt booleanColor: Int = ContextCompat.getColor(
            context,
            R.color.default_boolean_color
        ),
        @ColorInt urlColor: Int = ContextCompat.getColor(context, R.color.default_url_color),
        @ColorInt nullColor: Int = ContextCompat.getColor(context, R.color.default_null_color)
    ) {
        with(adapter) {
            this.textSize = when {
                textSize < MIN_TEXT_SIZE -> MIN_TEXT_SIZE
                textSize > MAX_TEXT_SIZE -> MAX_TEXT_SIZE
                else -> textSize
            }
            this.textColor = textColor
            this.keyColor = keyColor
            this.stringColor = stringColor
            this.numberColor = numberColor
            this.booleanColor = booleanColor
            this.urlColor = urlColor
            this.nullColor = nullColor
            // Refresh the list
            notifyDataSetChanged()
        }
    }

    private companion object {
        const val MIN_TEXT_SIZE = 12.0 F
        const val MAX_TEXT_SIZE = 24.0 F}}Copy the code

digression

A web site that visualizes regular expressions is as follows:

Regexper

My GitHub: TanJiaJunBeyond

Common Android Framework: Common Android framework

My nuggets: Tan Jiajun

My simple book: Tan Jiajun

My CSDN: Tan Jiajun