当前位置:首页 » 《随便一记》 » 正文

2021-09-11_打的不好,请指教的博客

27 人参与  2021年12月27日 17:57  分类 : 《随便一记》  评论

点击全文阅读


单层RecycleView实现列表的展开与折叠操作

  • 目标需求
    • Activity的创建
    • Model的创建
    • Holder的创建
    • Adapter的创建
    • 效果展示
  • 总结

目标需求

在Android开发中使用单层的RecycleView实现:一、单击主目录展开其子目录列表,再次单击关闭主目录列表;二、单击子目录,显示子目录的内容或者多子目录进行下载等其他操作。

Activity的创建

我们只需要对创建一个MainActivity,对功能进行一个简单的展示即可。MainActivity主要包含:
1、recycLayoutManager 定义一个布局管理器,用来对recycleview的布局进行约束;
2、mListData 定义一个List用来承载数据;
3、myAdapter 定义一个MyRecycAdapter,自定义的Adapter是关键,后面将详细介绍
4、最后还要定义一个RecycleView

当点击事件发生时,我们要根据点击目录的类型进行判断,可以为两大类:
1、当点击对象为主目录时,需要判断当下的主目录对象是否展开。如果展开,点击下,则需要关闭该目录,则将其对应的子目录从mListData中删除;反之,点击下,怎需要展开该目录,则将其对应的子目录在mListData的指定位置添加,然后更新数据;
2、当点击对象为子目录时,需要判断当下的子目录是否被选中,如果选中,点击下,则需要取消选中;反之,点击下,则需要确定选中。

在这里插入MainActivity的主要代码片

class MainActivity : AppCompatActivity() {
    val TAG = "MainActivity"
    var recycLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
    var mListData = ArrayList<Any>()
    var myAdapter : MyRecycAdapter ?= null

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

        initData()

        myAdapter = MyRecycAdapter(mListData)
        recyclerView_type.adapter = myAdapter
        myAdapter!!.onItemClickListener = object : MyRecycAdapter.OnItemClickListener {
                override fun onItemClick(obj: Any, position: Int) {
                    if (obj is MainCategory){
                        val mainCategory = obj as MainCategory
                        if (mainCategory.isShown!!){
                            mainCategory.isShown = false
                            val children = mainCategory.children
                            Log.e(TAG, "onItemClick: ${children.toString()}" )
                            mListData.removeAll(children!!)
                            myAdapter!!.notifyDataSetChanged()
                        }else{
                            mainCategory.isShown = true
                            val children = mainCategory.children
                            mListData.addAll(position+1, children!!)
                            myAdapter!!.notifyDataSetChanged()
                        }
                    }else if (obj is ChildCategory){
                        val childCategory = obj as ChildCategory
                        childCategory.isSelected = !childCategory.isSelected!!
                        if (childCategory.isSelected!!){
                            Toast.makeText(baseContext, childCategory.toString(), Toast.LENGTH_SHORT).show()
                        }
                    }
                }
            };
    }
}

Model的创建

我们需要创建主目录和子目录对应的model,这些model有助于建立起来主目录与子目录的联系,主目录方面主要包含以下四个参数:
1、 id,主目录的编号,用来标记子目录中的ParentId;
2、name,主目录的名称,用来展示主目录的内容;
3、isShown,主目录是否展开,用来判断主目录是否展开
4、children,主目录对应的子目录,用来点击主目录的时候,将其对应的子目录展开

class MainCategory {

    var id : String ?= null
    var name : String ?= null
    var isShown : Boolean ?= null
    var children : ArrayList<ChildCategory> ?= null

    constructor() {}

    constructor(id : String, name : String, isShown : Boolean, children : ArrayList<ChildCategory>){
        this.id = id
        this.name = name
        this.isShown = isShown
        this.children = children
    }
}

子目录对应的参数如下:
1、id,子目录编号
2、name,子目录的名字
3、content,子目录的内容,用来简单描述子目录
4、parentId,子目录对应主目录的ID,用来寻找到其父目录
5;isSelected,子目录是否被选中,选中的情况下,将选中的icon展示出来。

class ChildCategory {

    var id : String ?= null
    var name : String ?= null
    var content : String ?= null
    var parentId : String ?= null
    var isSelected : Boolean ?= null

    constructor(){}
    constructor(id: String?, name: String?, content: String?, parentId: String?, isSelected : Boolean?) {
        this.id = id
        this.name = name
        this.content = content
        this.parentId = parentId
        this.isSelected = isSelected
    }

    override fun toString(): String {
        return "ChildCategory(id=$id, name=$name, content=$content, parentId=$parentId, isSelected=$isSelected)"
    }
}

Holder的创建

因为主目录与子目录的布局不同,Holder的创建显得格外的重要,主目录和子目录分别对应着不同的Holder。但是两者又不同完全割裂开来,因为在Adapter中要添加一个泛型类型。为了解决此问题,我们首先创建一个BaseHolder让它简单继承ViewHolder,代码如下:

open class BaseViewHolder(itemView: View) : ViewHolder(itemView) {}

让后让MainCategoryHolder和ChildCategoryHolder分别继承BaseHolder,这样我们就可以在Adapter的泛型类型中只写BaseHolder即可,代码如下:

    class MainCategoryHolder(itemView: View) : BaseViewHolder(itemView) {
        init {
            var tv_maincategory: TextView = itemView.findViewById(R.id.tv_mainCategory)
        }
    }
    class ChildCategoryHolder(itemView: View) : BaseViewHolder(itemView) {

        init {
            var tv_childcategory: TextView = itemView.findViewById(R.id.tv_childCategory)
            var img_selected: ImageView = itemView.findViewById(R.id.img_selected)
        }
    }

在MainCategoryHolder和ChildCategoryHolder中可以将main_item.xml和child_item.xml文件分别联系起来,进行实例化;格外注意,这三个类是在Adapter中以静态类的形式存在的。

Adapter的创建

接下来到了本文中最为重要的一个环节了,Adapter类的创建。我们需要自定义一个Adapter用来适配。因为存在不同类型的Holder,因此我们首先定义两个常数,用来区别两种类型的Holder:
1、val TYPE_MAINCATEGORY: Int = 0;
2、val TYPE_CHILDCATEGORY: Int = 1;
然后,我们需要复写getItemViewType()方法,让它根据不同View类型,返回不同的参数,当它为MainCategory则返回TYPE_MAINCATEGORY;反之,则返回TYPE_CHILDCATEGORY,代码如下:

    override fun getItemViewType(position: Int): Int {
        super.getItemViewType(position)
        val any = mDataList[position]
        if (any is MainCategory) {
            return TYPE_MAINCATEGORY
        } else if (any is ChildCategory) {
            return TYPE_CHILDCATEGORY
        } else {
            Log.e(TAG, "getItemViewType is error!")
            return -1
        }
    }

然后就是根据不同类型的ItemView创建不同类型的Holder,这里就要复写onCreateViewHolder(),根据不同类型的viewType,分别创建不同的View,不同得View对应着不同类型的XML文件,然后将View传进Holder里面,返回。

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
        when (viewType) {
            TYPE_MAINCATEGORY -> {
                var view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.maincategory_item, parent, false)
                return MainCategoryHolder(view)
            }
            TYPE_CHILDCATEGORY -> {
                var view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.childcategory_item, parent, false)
                return ChildCategoryHolder(view)
            }
            else -> {
                return null!!
            }
        }
    }

接下来就是绑定ViewHolder,将Model中的数据与View中的对象进行一对一的绑定,如果是MainCategoryHolder,则其XML文件中对应的的控件只有一个TextView,将TextView中的内容更新为Model中的name内容(也可为Model中的其他内容);若为ChildCategoryHolder的时候,其对应的XML文件中存在TextView与ImageView两个控件,我们不仅需要将TextView中的内容与Model中的数据对应起来,还需要根据Model的isSelected数据来判断此时ImageView是否应该展露出来。最为关键的是为了将自定义Adapter中的数据传递出去,我们还需要在自定义的Adapter中定义一个接口,和其对应的set方法。

    fun SetOnItemClickListener(onItemClick: OnItemClickListener) {
        this.onItemClickListener = onItemClickListener
    }
    public interface OnItemClickListener {
        fun onItemClick(obj: Any, position: Int);
    }
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
        if (holder is MainCategoryHolder) {
            val mainCategoryHolder = holder as MainCategoryHolder
            val mainCategory = mDataList[position] as MainCategory
            mainCategoryHolder.itemView.tv_mainCategory.text = mainCategory.name
            mainCategoryHolder.itemView.setOnClickListener (View.OnClickListener {
                if (onItemClickListener != null){
                    onItemClickListener?.onItemClick(mainCategory, position)
                }
            })

        } else if (holder is ChildCategoryHolder) {
            val childCategoryHolder = holder as ChildCategoryHolder
            val childCategory = mDataList[position] as ChildCategory
            childCategoryHolder.itemView.tv_childCategory.text = childCategory.name
            if (childCategory.isSelected!!){
                childCategoryHolder.itemView.img_selected.visibility = View.VISIBLE
            }else{
                childCategoryHolder.itemView.img_selected.visibility = View.INVISIBLE
            }
            childCategoryHolder.itemView.setOnClickListener(View.OnClickListener {
                if (onItemClickListener != null) {
                    onItemClickListener?.onItemClick(childCategory, position)
                }
                if (childCategory.isSelected!!){
                    childCategoryHolder.itemView.img_selected.visibility = View.VISIBLE
                }else{
                    childCategoryHolder.itemView.img_selected.visibility = View.INVISIBLE
                }
            })
        } else {

        }
    }

效果展示

图片:
Alt

总结

RecycleView是一个Android开发中非常重要的一个插件,这里简单介绍一下其在多级目录中的使用。如果您觉得有用欢迎点赞、收藏、赞赏,您的鼓励是我前进的动力!需要完整Demo,评论区留言,留下邮箱,并支付五元辛苦费。未经同意,不得转载!
在这里插入图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/32226.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1