Android fully conforms to the rules, but the troublesome Jason is mapped into a tree structured and collapsible list?

Posted by ma5ect on Fri, 04 Mar 2022 06:16:40 +0100

Turn:

Android fully conforms to the rules, but the troublesome Jason is mapped into a tree structured and collapsible list?

Article catalogue

    • Effect drawing first
    • preface
      • Layer by layer addView mode

Effect drawing first

preface

A friend asked me a few days ago how to implement a tree list according to a Json parsing that fully conforms to the rules but has a headache. See the format below. For some Android developers, this Json may not be friendly and there is no way to directly convert it into an entity class. In fact, it is not difficult to map this string of Json parsing into a collapsible list!

Code submitted to Github: https://github.com/ThirdGoddess/AndroidTree

{
     
     
    "code":"200",
    "message":"success",
    "data":[
        {
     
     
            "id":"1001",
            "title":"No. 1",
            "next":[
                {
     
     
                    "id":"10011",
                    "title":"No. 1-1"
                },
                {
     
     
                    "id":"10012",
                    "title":"No. 1-2",
                    "next":[
                        {
     
     
                            "id":"100121",
                            "title":"No. 1-2-1",
                            "next":[
                                {
     
     
                                    "id":"1001211",
                                    "title":"No. 1-2-1-1"
                                },
                                {
     
     
                                    "id":"1001212",
                                    "title":"No. 1-2-1-2"
                                },
                                {
     
     
                                    "id":"1001213",
                                    "title":"No. 1-2-1-3"
                                },
                                {
     
     
                                    "id":"1001214",
                                    "title":"No. 1-2-1-4"
                                },
                                {
     
     
                                    "id":"1001215",
                                    "title":"No. 1-2-1-5"
                                }
                            ]
                        },
                        {
     
     
                            "id":"100122",
                            "title":"No. 1-2-2"
                        },
                        {
     
     
                            "id":"100123",
                            "title":"No. 1-2-3",
                            "next":[
                                {
     
     
                                    "id":"1001231",
                                    "title":"No. 1-2-3-1"
                                },
                                {
     
     
                                    "id":"1001232",
                                    "title":"No. 1-2-3-2"
                                },
                                {
     
     
                                    "id":"1001233",
                                    "title":"No. 1-2-3-3"
                                },
                                {
     
     
                                    "id":"1001234",
                                    "title":"No. 1-2-3-4"
                                },
                                {
     
     
                                    "id":"1001235",
                                    "title":"No. 1-2-3-5"
                                }
                            ]
                        }
                    ]
                },
                {
     
     
                    "id":"10013",
                    "title":"No. 1-3"
                }
            ]
        },
        {
     
     
            "id":"1002",
            "title":"No. 2"
        },
        {
     
     
            "id":"1003",
            "title":"No. 3"
        },
        {
     
     
            "id":"1004",
            "title":"No. 4",
            "next":[
                {
     
     
                    "id":"10041",
                    "title":"No. 4-1"
                },
                {
     
     
                    "id":"10042",
                    "title":"No. 4-2"
                }
            ]
        },
        {
     
     
            "id":"1005",
            "title":"No. 5"
        }
    ]
}

What should Jason think when he gets this string of uncertain levels? With what to analyze? What controls should I use?

Layer by layer addView mode

In fact, Gson can be directly used for parsing, but this entity class should be written by itself:

package com.example.myapplication;

import java.util.List;

public class DataBean {
     
     

    private String code;
    private String message;
    private List<Data> data;

    public String getCode() {
     
     
        return code;
    }

    public void setCode(String code) {
     
     
        this.code = code;
    }

    public String getMessage() {
     
     
        return message;
    }

    public void setMessage(String message) {
     
     
        this.message = message;
    }

    public List<Data> getData() {
     
     
        return data;
    }

    public void setData(List<Data> data) {
     
     
        this.data = data;
    }

    public static class Data {
     
     
        private String id;
        private String title;
        private List<Data> next;//The point is here

        public String getId() {
     
     
            return id;
        }

        public void setId(String id) {
     
     
            this.id = id;
        }

        public String getTitle() {
     
     
            return title;
        }

        public void setTitle(String title) {
     
     
            this.title = title;
        }

        public List<Data> getNext() {
     
     
            return next;
        }

        public void setNext(List<Data> next) {
     
     
            this.next = next;
        }
    }
}

(OpenParam.json is the JSON string)

Parse using Gson:

Kotlin:

 val dataBean = Gson().fromJson(OpenParam.json, DataBean().javaClass)

Java:

 DataBean dataBean = new Gson().fromJson(OpenParam.json, DataBean.class)

Now that it has been resolved, it can be implemented by passing back gradually addView(), and judge whether the next field is null! But before recursion starts, analyze the layout!

Since you want to nest level by level, first come to a LinearLayout. Of course, this list is slidable. Just nest a ScrollView in the outer layer. The Activity layout is like this:

     
     
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints">

        <LinearLayout
            android:id="@+id/treeLayout"
            android:layout_width="match_parent"
             android:orientation="vertical"
            android:layout_height="wrap_content">

        
       
       LinearLayout>

    
        
        ScrollView>

 
         androidx.constraintlayout.widget.ConstraintLayout>

After analyzing each item, there are two cases: one is the item with layout, and the other is the item without layout. When there is nesting, that is, there is a next field, you can use the item with layout, and vice versa! Then the two layouts are as follows:


With layout:

     
     
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/flag"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginLeft="32dp"
            android:padding="8dp"
            android:src="@mipmap/open" />

        <TextView
            android:id="@+id/title"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_marginTop="1dp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:paddingRight="32dp"
            android:textColor="#333333" />

    
       
       LinearLayout>

    <LinearLayout
        android:id="@+id/nextLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="32dp"
        android:orientation="vertical" />


        
        LinearLayout>

Without layout:

     
     
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/info"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginLeft="72dp"
        android:layout_marginRight="32dp"
        android:gravity="center_vertical"
        android:textColor="#333333" />


       
       LinearLayout>

After that, it will be implemented recursively according to the entity class, loop through, judge whether there is a next field, and make two cases, such as the code between lines 37 and 69! If there are sub nodes, use the item with sub layout; otherwise, use another one!

package com.example.myapplication

import android.animation.ObjectAnimator
import android.os.Bundle
import android.view.LayoutInflater.from
import android.view.View
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.item_text.view.*
import kotlinx.android.synthetic.main.item_tree.view.*

class MainActivity : AppCompatActivity() {
     
     

    lateinit var objectAnimator: ObjectAnimator

    override fun onCreate(savedInstanceState: Bundle?) {
     
     
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //Parse Jason
        val dataBean = Gson().fromJson(OpenParam.json, DataBean().javaClass)

        //Create View
        createView(dataBean.data, treeLayout)
    }

    /**
     * Create layout recursively
     */
    private fun createView(dataList: MutableList<DataBean.Data>, linearLayout: LinearLayout) {
     
     
        for (i in 0 until dataList.size) {
     
     
            val title = dataList[i].title
            val next = dataList[i].next
            if (null != next) {
     
     
                val childLayout = from(this).inflate(R.layout.item_tree, null, false)
                childLayout.title.text = title

                //Expand and close click events
                childLayout.title.setOnClickListener {
     
     
                    if (childLayout.nextLayout.isGone) {
     
     

                        //open
                        childLayout.nextLayout.visibility = View.VISIBLE

                        //Add points to expand animation
                        objectAnimator = ObjectAnimator.ofFloat(childLayout.flag, "rotation", 0f)
                        objectAnimator.duration = 400
                        objectAnimator.start()
                    } else {
     
     

                        //hide
                        childLayout.nextLayout.visibility = View.GONE

                        //Add point to close animation
                        objectAnimator = ObjectAnimator.ofFloat(childLayout.flag, "rotation", -90f)
                        objectAnimator.duration = 400
                        objectAnimator.start()
                    }
                }
                createView(next, childLayout.nextLayout)
                linearLayout.addView(childLayout)
            } else {
     
     
                val textLayout = from(this).inflate(R.layout.item_text, null, false)
                textLayout.info.text = title
                linearLayout.addView(textLayout)
            }
        }
    }
}

In this way, this is applicable to the conventional folding list. If you need to load more, you can directly judge whether the ScrollView scrolls to the bottom and whether the last network loading is completed. If the conditions are met, you can call 27 lines of code again for insertion! No more explanations here. Friends who don't understand can ask me directly in the comment area!

Turn:

Android fully conforms to the rules, but the troublesome Jason is mapped into a tree structured and collapsible list?


--Posted from Rpc