之前介绍了通过多ViewHolder实现单列表不同的效果,这次我们通过另一个方法实现同样的效果,那就是RecyclerView 1.2.0版本推出的ConcatAdapter 适配器。
什么是ConcatAdapter?
ConcatAdapter是RecyclerView 1.2.0版本推出的新适配器,它可以将多个RecyclerView.Adapter组合在一起实现viewtype的效果,更方便;
RecyclerView 1.2.0版本更新了什么?
- ConcatAdapter 适配器;
- 弃用了 ViewHolder.getAdapterPosition取而代之的是getBindingAdapterPosition 会返回相对于其所绑定的适配器的位置、getAbsoluteAdapterPosition 会返回相对于整个 RecyclerView 的位置;
- 延迟状态恢复:现在,RecyclerView 适配器可以推迟状态恢复的时间,直到其内容加载完毕;
ConcatAdapter如何使用?
一. 依赖库引入
implementation 'androidx.recyclerview:recyclerview:1.2.1'
二. 将之前写的ViewHolder转化成Adapter
- 底部加载adapter样式
class OneAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class LoadMoreViewHolder(view: View): RecyclerView.ViewHolder(view) {}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = LoadMoreViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_load_more, parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {}
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
(holder.itemView.layoutParams as StaggeredGridLayoutManager.LayoutParams).isFullSpan = true
}
override fun getItemCount(): Int = 1
}
- 顶部横向滚动adapter
class TwoAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = Type0ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_top_list, parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
}
override fun getItemCount(): Int = 1
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
(holder.itemView.layoutParams as StaggeredGridLayoutManager.LayoutParams).apply {
isFullSpan = true
width = holder.itemView.context.resources.displayMetrics.widthPixels
holder.itemView.layoutParams = this
}
}
inner class Type0ViewHolder(view: View): RecyclerView.ViewHolder(view) {
init {
val recyclerView = itemView.findViewById<RecyclerView>(R.id.top_list)
recyclerView.layoutManager = LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false)
LinearSnapHelper().apply {
attachToRecyclerView(recyclerView)
}
recyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
/**
* Called when RecyclerView needs a new [ViewHolder] of the given type to represent
* an item.
*
*
* This new ViewHolder should be constructed with a new View that can represent the items
* of the given type. You can either create a new View manually or inflate it from an XML
* layout file.
*
*
* The new ViewHolder will be used to display items of the adapter using
* [.onBindViewHolder]. Since it will be re-used to display
* different items in the data set, it is a good idea to cache references to sub views of
* the View to avoid unnecessary [View.findViewById] calls.
*
* @param parent The ViewGroup into which the new View will be added after it is bound to
* an adapter position.
* @param viewType The view type of the new View.
*
* @return A new ViewHolder that holds a View of the given view type.
* @see .getItemViewType
* @see .onBindViewHolder
*/
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerView.ViewHolder = Type3ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_card, parent, false))
/**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the [ViewHolder.itemView] to reflect the item at the given
* position.
*
*
* Note that unlike [android.widget.ListView], RecyclerView will not call this method
* again if the position of the item changes in the data set unless the item itself is
* invalidated or the new position cannot be determined. For this reason, you should only
* use the `position` parameter while acquiring the related data item inside
* this method and should not keep a copy of it. If you need the position of an item later
* on (e.g. in a click listener), use [ViewHolder.getAdapterPosition] which will
* have the updated adapter position.
*
* Override [.onBindViewHolder] instead if Adapter can
* handle efficient partial bind.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param position The position of the item within the adapter's data set.
*/
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {}
/**
* Returns the total number of items in the data set held by the adapter.
*
* @return The total number of items in this adapter.
*/
override fun getItemCount(): Int = 5
/**
* Called when a view created by this adapter has been attached to a window.
*
*
* This can be used as a reasonable signal that the view is about to be seen
* by the user. If the adapter previously freed any resources in
* [onViewDetachedFromWindow][.onViewDetachedFromWindow]
* those resources should be restored here.
*
* @param holder Holder of the view being attached
*/
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
super.onViewAttachedToWindow(holder)
val lp = holder.itemView.layoutParams
val width = holder.itemView.context.resources.displayMetrics.widthPixels / 1.5
lp.width = width.toInt()
lp.height = (width / 3 * 4).toInt()
holder.itemView.layoutParams = lp
}
}
}
}
inner class Type3ViewHolder(view: View): RecyclerView.ViewHolder(view) {}
}
- 瀑布流主adapter
class ThreeAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = Type3ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_card, parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {}
override fun getItemCount(): Int = 30
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
val lp = holder.itemView.layoutParams
val width = holder.itemView.context.resources.displayMetrics.widthPixels / 2
lp.width = width
lp.height = width / 3 * 4
holder.itemView.layoutParams = lp
}
inner class Type3ViewHolder(view: View): RecyclerView.ViewHolder(view) {}
}
- 单个banner
class FourAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = Type1ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_card, parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {}
override fun getItemCount(): Int = 3
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
(holder.itemView.layoutParams as StaggeredGridLayoutManager.LayoutParams).apply {
isFullSpan = true
width = holder.itemView.context.resources.displayMetrics.widthPixels
height = width / 4
holder.itemView.layoutParams = this
}
}
inner class Type1ViewHolder(view: View): RecyclerView.ViewHolder(view) {}
}
三. ConcatAdapter的使用
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
recyclerView.adapter = ConcatAdapter(
TwoAdapter(),
FourAdapter(),
ThreeAdapter(),
OneAdapter(),
)
只需要将我们不同的adapter放入ConcatAdapter中即可实现;
ConcatAdapter还有带config的构造方法,isolateViewTypes用于不同adapter是否共享viewtype;stableIdMode为ConcatAdapter运作方式,adapter是否隔离,meigeadapter是否有单独的id等;
完整项目地址
github:github-RecyclerViewStyle
gitlab:gitlab-RecyclerViewStyle