Android app 优雅展示复杂列表

作者: flaming | 来源:发表于2020-04-26 15:20 被阅读0次

    问题描述

    Android 应用列表展示功能非常常见。特别是电商 新闻 这类的应用。一般主页就是一个复杂的列表。复杂列表一般包含多种展示样式,每一种样式或多种样式 对应一种数据结构,每种样式包含N个N>=0 item。

    示例效果图

    为了简化问题,每种颜色对应一种展示效果。

    实现思路

    经过不断的重构迭代,实现了一个非常优雅的 RecyclerView Adapter。可以展示各种复杂列表,而且代码简洁易懂,易扩展,代码高度复用。
    列表中每一个样式的核心逻辑有两个,一个是样式对应的布局,也就是展示效果,另一个是布局中控件如何和数据关联。这两个功能对应接口ViewTypeDelegateAdapter 中 onCreateViewHolder和onBindViewHolder。

     interface ViewTypeDelegateAdapter {
            fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder
            fun onBindViewHolder(holder: CommonViewHolder, position: Int, data: Any?)
        }
    

    总的实现思路是基于委托模式,每一种展示样式委托给一个轻量级的DelegateAdapter,实现展示效果。这个类非常简单,只需要实现接口中的两个函数。

    核心功能由MultiTypeAdapter 实现,它负责根据每种展示样式委托给对应的代理。还有RecyclerView.Adapter
    中的一些其他逻辑

    为了实现委托定义了一个泛型数据结构

        data class CommonAdapterItem(val data: Any, val type: Int, var spanSize: Int = 1)
    

    把要展示的数据使用CommonAdapterItem 包装一下,设置对应的展示样式和GridLayoutManager 对应的spanCount。然后把数据塞给 MultiTypeAdapter ,由它委托给对应的实现类。

    核心代码和demo

    代码已经在多个项目中使用过,经过多次优化已经非常的简单高效。为了方便查看,把所有的功能放在了一个类中,即使这样也不到200行代码

    const val RED = 1
    const val GREEN = 2
    const val BLUE = 3
    const val YELLOW = 4
    const val PURPLE = 5
    
    
    class MultiTypeAdapter : RecyclerView.Adapter<CommonViewHolder>() {
    
        interface ViewTypeDelegateAdapter {
            fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder
            fun onBindViewHolder(holder: CommonViewHolder, position: Int, data: Any?)
        }
    
        private val mContent: MutableList<CommonAdapterItem> = mutableListOf()
    
        private val mFactory = DelegateAdapterFactory()
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder {
            return mFactory.getDelegateAdapter(viewType).onCreateViewHolder(parent, viewType)
        }
    
        override fun onBindViewHolder(holder: CommonViewHolder, position: Int) {
            val type = mContent[position].type
            mFactory.getDelegateAdapter(type)
                .onBindViewHolder(holder, position, mContent[position].data)
        }
    
        override fun getItemViewType(position: Int): Int = mContent[position].type
    
        override fun getItemCount(): Int = mContent.size
    
        fun addItems(
            items: Collection<Any>?,
            type: Int = RED,
            append: Boolean = false,
            spanSize: Int = 1
        ) {
            items?.let {
                if (!append) {
                    mContent.clear()
                }
                mContent.addAll(transform(items, type, spanSize))
                notifyDataSetChanged()
            }
        }
    
    //    fun setOnItemClick(callback: (position: Int, data: Any?, action: Int, extra: Any?) -> Unit) {
    //        mFactory.onItemClick = callback
    //    }
    //
    //    fun setOnItemClick(callback: (position: Int, data: Any?, action: Int) -> Unit) {
    //        mFactory.onItemClick = { position, data, action, _ ->
    //            callback(position, data, action)
    //        }
    //    }
    
        fun getItemType(position: Int): Int = mContent[position].type
    
        fun clear() {
            mContent.clear()
            notifyDataSetChanged()
        }
    
        fun removeItem(position: Int) {
            mContent.removeAt(position)
            notifyItemRemoved(position)
        }
    
        fun changeItem(position: Int, item: Any?, type: Int, spanSize: Int = 1) {
            item?.let {
                if (position < mContent.size) {
                    mContent[position] = transform(item, type, spanSize)
                    notifyItemChanged(position)
                }
            }
        }
    
    //    fun addItem(item: Collection<Any>?, type: Int = RED, append: Boolean = false) {
    //        item?.let {
    //            if (!append) {
    //                mContent.clear()
    //            }
    //            mContent.add(CommonAdapterItem(item.toMutableList(), type))
    //            notifyDataSetChanged()
    //        }
    //    }
    
        fun addItem(item: Any?, type: Int = RED, append: Boolean = false, spanSize: Int = 1) {
            item?.let {
                if (!append) {
                    mContent.clear()
                }
                mContent.add(transform(item, type, spanSize))
                notifyDataSetChanged()
            }
        }
    
        override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
            super.onAttachedToRecyclerView(recyclerView)
            val manager = recyclerView.layoutManager
            if (manager is GridLayoutManager) {
                manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
                    override fun getSpanSize(position: Int): Int {
                        return mContent[position].spanSize
                    }
                }
            }
        }
    
        private fun transform(item: Any, type: Int, spanSize: Int = 1): CommonAdapterItem {
            return CommonAdapterItem(item, type, spanSize)
        }
    
        private fun transform(
            items: Collection<Any>,
            type: Int,
            spanSize: Int = 1
        ): List<CommonAdapterItem> {
            return items.map { CommonAdapterItem(it, type, spanSize) }
        }
    
        data class CommonAdapterItem(val data: Any, val type: Int, var spanSize: Int = 1)
    
        open class BaseDelegateAdapter(protected val layoutId: Int) : ViewTypeDelegateAdapter {
    
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder {
                val view = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
                return CommonViewHolder(view)
            }
    
            override fun onBindViewHolder(holder: CommonViewHolder, position: Int, data: Any?) {}
        }
    
        class DelegateAdapterFactory {
            private val adapterCache = SparseArray<ViewTypeDelegateAdapter>() //提高性能
    
            fun getDelegateAdapter(type: Int): ViewTypeDelegateAdapter {
                var adapter = adapterCache[type]
                if (adapter == null) {
                    adapter = when (type) {
                        RED, GREEN, BLUE, YELLOW, PURPLE -> object :
                            BaseDelegateAdapter(R.layout.viewholder_example) {
                            override fun onBindViewHolder(
                                holder: CommonViewHolder,
                                position: Int,
                                data: Any?
                            ) {
                                super.onBindViewHolder(holder, position, data)
                                if(data is String){
                                    holder.get<View>(R.id.content).setBackgroundColor(Color.parseColor(data))
                                }
                            }
                        }
                        else -> {
                            BaseDelegateAdapter(android.R.layout.simple_list_item_1)
                        }
                    }
                    adapterCache.put(type, adapter)
    
                }
                return adapter
            }
        }
    }
    
    

    demo对应的示例代码

           val adapter = MultiTypeAdapter()
            recyclerView.layoutManager = GridLayoutManager(this,4)
            recyclerView.adapter = adapter
            adapter.addItem("#FF0000",RED, true,4)
            val greenList = MutableList(10){
                "#00FF00"
            }
            val blueList = MutableList(5){
                "#0000FF"
            }
            val yellowList = MutableList(5){
                "#FFFF00"
            }
            val purpleList = MutableList(5){
                "#AA66CC"
            }
            adapter.addItems(greenList, GREEN,true,1)
            adapter.addItems(blueList, BLUE,true,4)
            adapter.addItems(yellowList, YELLOW,true,2)
            adapter.addItems(purpleList, PURPLE, true,3)      
    

    相关文章

      网友评论

        本文标题:Android app 优雅展示复杂列表

        本文链接:https://www.haomeiwen.com/subject/qpgdwhtx.html