一、实现效果
二、项目
三、引入依赖
1、在app
的build.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 | 是 |
这依赖包还需要得到要添加,在Project
的build.gradle
在添加以下代码,不添加就不行
allprojects {
repositories {
...
maven { url "https://jitpack.io" }//加上
}
}
四、实现源码
1、实体类
我用的是greenDAO
:Android 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_PAYLOAD
,AppConfig.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>