問題描述
Android 應(yīng)用列表展示功能非常常見经柴。特別是電商 新聞 這類的應(yīng)用猴娩。一般主頁就是一個復(fù)雜的列表。復(fù)雜列表一般包含多種展示樣式饿肺,每一種樣式或多種樣式 對應(yīng)一種數(shù)據(jù)結(jié)構(gòu)蒋困,每種樣式包含N個N>=0 item。
為了簡化問題敬辣,每種顏色對應(yīng)一種展示效果雪标。
實現(xiàn)思路
經(jīng)過不斷的重構(gòu)迭代,實現(xiàn)了一個非常優(yōu)雅的 RecyclerView Adapter溉跃〈迮伲可以展示各種復(fù)雜列表,而且代碼簡潔易懂撰茎,易擴展嵌牺,代碼高度復(fù)用。
列表中每一個樣式的核心邏輯有兩個龄糊,一個是樣式對應(yīng)的布局逆粹,也就是展示效果,另一個是布局中控件如何和數(shù)據(jù)關(guān)聯(lián)炫惩。這兩個功能對應(yīng)接口ViewTypeDelegateAdapter 中 onCreateViewHolder和onBindViewHolder僻弹。
interface ViewTypeDelegateAdapter {
fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder
fun onBindViewHolder(holder: CommonViewHolder, position: Int, data: Any?)
}
總的實現(xiàn)思路是基于委托模式,每一種展示樣式委托給一個輕量級的DelegateAdapter他嚷,實現(xiàn)展示效果蹋绽。這個類非常簡單芭毙,只需要實現(xiàn)接口中的兩個函數(shù)。
核心功能由MultiTypeAdapter 實現(xiàn)蟋字,它負(fù)責(zé)根據(jù)每種展示樣式委托給對應(yīng)的代理稿蹲。還有RecyclerView.Adapter
中的一些其他邏輯
為了實現(xiàn)委托定義了一個泛型數(shù)據(jù)結(jié)構(gòu)
data class CommonAdapterItem(val data: Any, val type: Int, var spanSize: Int = 1)
把要展示的數(shù)據(jù)使用CommonAdapterItem 包裝一下,設(shè)置對應(yīng)的展示樣式和GridLayoutManager 對應(yīng)的spanCount鹊奖。然后把數(shù)據(jù)塞給 MultiTypeAdapter 苛聘,由它委托給對應(yīng)的實現(xiàn)類。
核心代碼和demo
代碼已經(jīng)在多個項目中使用過忠聚,經(jīng)過多次優(yōu)化已經(jīng)非常的簡單高效设哗。為了方便查看,把所有的功能放在了一個類中两蟀,即使這樣也不到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對應(yīng)的示例代碼
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)