Android kotlin 用RecyclerView(androidx+BRVAH3.0.6+greenDAO)实现可扩展多级列表功能

一、实现效果

二、项目

三、引入依赖

1、在appbuild.gradle在添加以下代码
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.6',这个里面带的多级列表,直接调用就即可

BaseRecyclerViewAdapterHelper简称BRVAH

Android SDK是否支持BaseRecyclerViewAdapterHelper:3.0.6
android compileSdkVersion 29
android compileSdkVersion 30
android compileSdkVersion 31
android compileSdkVersion 32
android compileSdkVersion 33

这依赖包还需要得到要添加,在Projectbuild.gradle在添加以下代码,不添加就不行

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }//加上
    }
}

四、实现源码

1、实体类

我用的是greenDAOAndroid kotlin 进阶之如何用greenDAO实现增删改查数据(拷贝assets下的db文件到data/data/applicationId/databases目录),你自己按下这个改,我改的源码没完整,先看一下MyCode.java

package com.example.myapplication3.data;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.Generated;

@Entity(nameInDb = "TestNode", createInDb = false)
public class MyCode {
    @Property(nameInDb = "id")
    private String id;
    @Property(nameInDb = "code")
    private String code;
    @Property(nameInDb = "name")
    private String name;

    @Generated(hash = 1022365150)
    public MyCode(String id, String code, String name) {
        this.id = id;
        this.code = code;
        this.name = name;
    }

    @Generated(hash = 1008272223)
    public MyCode() {
    }

    public String getId() {
        return this.id;
    }

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

    public String getCode() {
        return this.code;
    }

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

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

2、SQLite数据库对应多级列表画解

拷贝assets下的db文件(Test)到data/data/applicationId/databases目录直接获取数据,SQLite数据库上改规律的多级列表,多级列表对应画解如下图,你自己按下这个改就即可
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、适配器

适配器继承BRVAH3.0.6(BaseNodeAdapter)RvAdapter.kt

package com.example.myapplication3.adapter

import com.chad.library.adapter.base.BaseNodeAdapter
import com.chad.library.adapter.base.entity.node.BaseNode
import com.example.myapplication3.adapter.section.provider.RootNodeProvider

class RvAdapter : BaseNodeAdapter() {

    init {
        addNodeProvider(RootNodeProvider())//在适配器加上BRVAH3.0.6里提供的节点
    }

    override fun getItemType(data: List<BaseNode>, position: Int): Int {
        return 0
    }
}

在适配器加上BRVAH3.0.6里提供的节点RootNodeProvider.kt代码难度不小又不熟,不要急,慢慢就看懂,最好用调试就即可

package com.example.myapplication3.adapter.section.provider

import android.graphics.Color
import android.text.Html
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.core.view.isGone
import com.chad.library.adapter.base.entity.node.BaseNode
import com.chad.library.adapter.base.provider.BaseNodeProvider
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.myapplication3.AppConfig.EXPAND_COLLAPSE_PAYLOAD
import com.example.myapplication3.R
import com.example.myapplication3.adapter.section.node.RootNode
import com.example.myapplication3.ext.rotation
import com.example.myapplication3.util.getCodeNode
import kotlinx.android.synthetic.main.item_expandable_group.view.*


class RootNodeProvider : BaseNodeProvider(){

    override val itemViewType: Int
        get() = 0

    override val layoutId: Int
        get() = R.layout.item_expandable_group

    override fun convert(helper: BaseViewHolder, item: BaseNode) {
        val entity: RootNode = item as RootNode

        setLeftMargin(helper, entity)

        setSerValue(helper,entity)

        // 增量刷新,使用动画变化箭头
        setArrowSpin(helper, item, false)
    }

    override fun convert(helper: BaseViewHolder, item: BaseNode, payloads: List<Any>) {
        for (payload in payloads) {
            if (payload is Int && payload == EXPAND_COLLAPSE_PAYLOAD) {
                // 增量刷新,使用动画变化箭头
                setArrowSpin(helper, item, true)
            }
        }
    }

    override fun onClick(helper: BaseViewHolder, view: View, data: BaseNode, position: Int) {

        // 这里使用payload进行增量刷新(避免整个item刷新导致的闪烁,不自然)
        getAdapter()!!.expandOrCollapse(position, true, true, EXPAND_COLLAPSE_PAYLOAD)

        val entity = data as RootNode

        if(entity.isExpanded){
            var child = entity.childNode
            //字节的没有数据 就添加
            if(child?.size?:0 == 0 ){
                var child = addChildNodes(entity)
                //添加子节点
                getAdapter()!!.nodeAddData(entity, 0, child)
            }
        }
    }

    /**
     * 添加子节点数据
     */
    private fun addChildNodes(item: RootNode): MutableList<BaseNode> {
        val list = getCodeNode(item.code)
        val child = mutableListOf<BaseNode>()
        list.forEach {
            var isHaveChild = getCodeNode(it.code).size > 0
            var childItem = RootNode(it.code,it.name,isHaveChild)
            child.add(childItem)
        }
        return child
    }

    //设置leftMargin
    private fun setLeftMargin(helper: BaseViewHolder, entity: RootNode) {
        helper.itemView.run {
            val layoutParams = tvTitle.layoutParams as LinearLayout.LayoutParams
            layoutParams.leftMargin = (entity.code.length - 2 )* 35 + 25
            tvTitle.layoutParams = layoutParams
            ivIcon.isGone = !entity.isHaveChild
        }
    }

    private fun setSerValue(helper: BaseViewHolder,entity: RootNode) {
        helper.itemView.run {
            var name = entity.name
            if(!entity.isHaveChild){
                tvTitle.textSize = 14f
                tvTitle.setTextColor(Color.RED)
            }else{
                tvTitle.textSize = 16f
                tvTitle.setTextColor(resources.getColor(R.color.black))
            }
            tvTitle.text = Html.fromHtml(name)
        }
    }
    private fun setArrowSpin(helper: BaseViewHolder, data: BaseNode, isAnimate: Boolean) {

        val entity: RootNode = data as RootNode
        val imageView = helper.getView<ImageView>(R.id.ivIcon)
        if (entity.isExpanded) {
            imageView.rotation(0f,isAnimate)
        } else {
            imageView.rotation(90f,isAnimate)
        }
    }
}

item布局:item_expandable_group.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/ripple_item_select2"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/node_ll"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:layout_marginRight="@dimen/dp_5"
            android:textSize="@dimen/sp_15"
            android:padding="@dimen/dp_8"
            android:paddingBottom="@dimen/dp_8"
            android:textColor="@color/black"
            android:layout_weight="1.0" />

        <ImageView
            android:id="@+id/ivIcon"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            app:srcCompat="@drawable/ic_right_black_24dp"
            tools:ignore="VectorDrawableCompat" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.8dp"
        android:background="@color/veeeeee" />
</LinearLayout>

在这里插入图片描述
ripple_item_select2.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/colorRipple" /><!-- colorRipple = E2E2E2 -->
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/bgColorPrimary" /><!-- bgColorPrimary= FFFFFF -->
        </shape>
    </item>

</selector>

在这里插入图片描述
箭头样式这是在android studio系统中添加资源图标

节点的根,像树一样,RootNode.kt

package com.example.myapplication3.adapter.section.node

import com.chad.library.adapter.base.entity.node.BaseExpandNode
import com.chad.library.adapter.base.entity.node.BaseNode

class RootNode(
    var code: String,
    var name: String,
    var isHaveChild: Boolean = false,
    override var childNode: MutableList<BaseNode>? = mutableListOf()
) :
    BaseExpandNode() {

    init {
        isExpanded = false
    }
}

EXPAND_COLLAPSE_PAYLOADAppConfig.kt

package com.example.myapplication3

object AppConfig {
    val EXPAND_COLLAPSE_PAYLOAD = 110
}

工具封装文件AppUtil.kt,获取MyCode节点数据,调用到getCodeNode函数

package com.example.myapplication3.util

import com.example.myapplication3.data.MyCode
import com.example.myapplication3.greendao.MyCodeDao
import net.lrwm.zhlf.factory.DaoFactory
import org.greenrobot.greendao.query.WhereCondition

/**
 * 获取 MyCode 节点数据
 */
fun getCodeNode(code: String?=null): MutableList<MyCode> {
    return if(code.isNullOrEmpty()){
        DaoFactory.instant.getMyCodeDao().queryBuilder().where(
            WhereCondition.StringCondition("length(code)==2")
        ).build().list()
    }else{
        DaoFactory.instant.getMyCodeDao().queryBuilder().where(
            MyCodeDao.Properties.Code.like("${code}_")
        ).build().list()
    }
}

箭头动画文件ViewExt.kt,调用到rotation函数

package com.example.myapplication3.ext

import android.view.View
import android.view.animation.DecelerateInterpolator
import androidx.core.view.ViewCompat

/**
 * 旋转
 * @param value 角度
 * @param isAnimate 动画
 */
fun View.rotation(value:Float, isAnimate: Boolean){
    if (isAnimate) {
        ViewCompat.animate(this).setDuration(200)
            .setInterpolator(DecelerateInterpolator())
            .rotation(value)
            .start()
    } else {
        this.rotation = value
    }
}

4、实现视图

MainActivity.kt,获取初始化的一级列表数据

package com.example.myapplication3

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.chad.library.adapter.base.entity.node.BaseNode
import com.example.myapplication3.adapter.RvAdapter
import com.example.myapplication3.adapter.section.node.RootNode
import com.example.myapplication3.file.File.copyDbFileFromAsset
import com.example.myapplication3.util.getCodeNode
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val mAdapter by lazy {
        RvAdapter()
    }

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

    private fun initFile() {
        val map = mutableMapOf<String, Boolean>()
        map["test.db"] = true
        copyDbFileFromAsset(this@MainActivity, map)
    }

    private fun initView() {
        mRecyclerView.adapter = mAdapter
    }

    private fun initNodeData() {

        val list = mutableListOf<BaseNode>()
        val prent = getCodeNode()
        prent.forEach {
            var item = RootNode(it.code, it.name, true)
            list.add(item)
        }
        mAdapter.setList(list)
    }
}

布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/mRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/bgColorSecondary"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彬sir哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值