Android 完全符合规则但很头疼的Json映射成一个树结构且可折叠的列表?

先上效果图

在这里插入图片描述

前言

前些天有个朋友问我,要实现一个树状的列表要怎么做,根据一个完全符合规则但是却很头疼的一个Json解析来实现,见下格式,对于有些Android开发者来说,这个Json或许并不友好,没有办法直接转成实体类,其实这一串Json解析映射成可折叠列表也并不难!

代码已提交至Github:https://github.com/ThirdGoddess/AndroidTree

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

拿到这一串不确定层级的Json该想什么?用什么去解析?该用什么控件?


逐层addView方式

其实可以直接使用Gson解析,不过这个实体类要自己手写一下:

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;//重点在这里

        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为那个json字符串 )

使用Gson解析:

Kotlin:

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

Java:

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

既然解析出来了,之后可以通过递归来逐渐addView()的方式实现,判断next字段是否为null即可!但要在递归开始之前,先要分析一下布局!

既然要逐级嵌套,先来一个LinearLayout,当然这个列表是可滑动的,外层嵌套一个ScrollView即可,Activity布局那就是这样的:

<?xml version="1.0" encoding="utf-8"?>
<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>

之后要分析每一个条目,有两种情况,一种是带子布局的item,一种是不带子布局的item,当遇到有嵌套的情况,即存在next字段,就可以使用带子布局的item,反之则是另一个!那么这两种布局就是如下:


带子布局的:

<?xml version="1.0" encoding="utf-8"?>
<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>


不带子布局的:

<?xml version="1.0" encoding="utf-8"?>
<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>

之后便是根据实体类来递归实现,循环遍历,判断是否存在next字段而做出两种情况,如在37行到69行之间代码!存在子节点使用带有子布局的item,反之使用另一个!

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)

        //解析Json
        val dataBean = Gson().fromJson(OpenParam.json, DataBean().javaClass)

        //创建View
        createView(dataBean.data, treeLayout)
    }

    /**
     * 递归创建布局
     */
    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

                //展开和关闭的点击事件
                childLayout.title.setOnClickListener {
                    if (childLayout.nextLayout.isGone) {

                        //展开
                        childLayout.nextLayout.visibility = View.VISIBLE

                        //添点展开动画
                        objectAnimator = ObjectAnimator.ofFloat(childLayout.flag, "rotation", 0f)
                        objectAnimator.duration = 400
                        objectAnimator.start()
                    } else {

                        //隐藏
                        childLayout.nextLayout.visibility = View.GONE

                        //添点关闭动画
                        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)
            }
        }
    }
}

这样便实现了,这种适用于常规的折叠列表,如果遇到需要加载更多的情况下,可以直接判断ScrollView是否滚动到底部,并且上次的网络加载是否完成,达成条件则再次调用27行代码进行插入即可!这里不再多做解释,不懂的朋友可以直接评论区问我!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容