前言
Recyclerview作为Android的常用控件之一,相信大家对它应该是十分熟悉了,不熟悉的朋友可以参考我之前发的文章 RecyclerView的基本使用,其中包括有单item及多Item的基本使用。
但是我们发现Recyclerview中没有提供方法直接添加headerView
和footView
。对于我们自己去使用来说还需要从getItemViewType
方法中进行重写,根据不同的类型添加不同的item布局,具体实现可以参考 RecyclerView的基本使用 中多item的使用。下面讨论下如何实现类似于ListView
的addHeaderView
方法来添加列表的头部和尾部。
设计与实现
既然类比于ListView
中的添加头部和尾部的方法,我们首先要先理清在listView
中是如何去实现的,直接上源码:
public void addHeaderView(View v, Object data, boolean isSelectable) {
if (v.getParent() != null && v.getParent() != this) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "The specified child already has a parent. "
+ "You must call removeView() on the child's parent first.");
}
}
final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mHeaderViewInfos.add(info);
mAreAllItemsSelectable &= isSelectable;
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
wrapHeaderListAdapterInternal();
}
// In the case of re-adding a header view, or adding one later on,
// we need to notify the observer.
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
}
从代码中可以看出,listView中对adapter进行了封装:HeaderViewListAdapter
,使用此类用来处理对HeaderView的处理。
查看此类代码,能够看出设计思想是通过使用了装饰模式的思路,对原有的adapter进行重新装饰,生成装饰类HeaderViewListAdapter
用来处理head view。其重点工作包括如下几项:
- 重新计算count。
- 重新计算position对应的View的不同。
了解了上述思路,接下来我们就可以动手去实现了,首先我们可以自定义一个继承自RecyclerView
的控件,定义可以添加头部和尾部的方法,如下:
class TestHeaderRecyclerView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {
private var headerViews: ArrayList<View> = ArrayList()
private var footerViews: ArrayList<View> = ArrayList()
public fun addHeaderView(headerView: View) {
headerViews.clear()
headerViews.add(headerView)
}
public fun addFooterView(footerView: View) {
footerViews.clear()
footerViews.add(footerView)
}
public fun setAdapter(testAdapter: TestAdapter) {
var adapter = TestHeaderAdapter(headerViews, footerViews, testAdapter)
this.adapter = adapter
}
}
然后实现一个adapter
类去装饰原有的adapter
,其实最终的实现方式也是通过设置adapter
中不同的itemType
去添加不同的布局View,示例代码如下:
class TestHeaderAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>, IWrapperAdapter {
private var mAdapter: TestAdapter? = null
private var headerViews: ArrayList<View>? = ArrayList()
private var footerViews: ArrayList<View>? = ArrayList()
private var currentPosition: Int = 0
constructor(
headViews: ArrayList<View>,
footViews: ArrayList<View>,
adapter: TestAdapter
) {
headerViews = headViews
footerViews = footViews
this.mAdapter = adapter
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
TYPE_HEADER -> {
if (headerViews?.get(0) != null) {
return HeadViewHolder(headerViews!![0])
}
}
TYPE_FOOTER -> {
if (footerViews?.get(0) != null) {
return FootViewHolder(footerViews!![0])
}
}
TYPE_INVALID -> {
}
else -> {
}
}
return mAdapter!!.onCreateViewHolder(parent, viewType)
}
override fun getItemCount(): Int {
return if (mAdapter != null)
mAdapter!!.itemCount + getFooterCount() + getHeaderCount()
else
getFooterCount() + getHeaderCount()
}
override fun getHeaderCount(): Int {
return headerViews?.size ?: 0
}
override fun getFooterCount(): Int {
return footerViews?.size ?: 0
}
override fun getItemViewType(position: Int): Int {
currentPosition = position
if (currentPosition < getHeaderCount())
return TYPE_HEADER
if (mAdapter != null) {
if (currentPosition < itemCount - getFooterCount()) {
return mAdapter!!.getItemViewType(currentPosition - getHeaderCount())
}
return TYPE_FOOTER
}
return TYPE_INVALID
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
var headCount = getHeaderCount()
if (position < headCount) {
return
}
var cPosition = position - headCount
if (mAdapter != null) {
var adapterCount = mAdapter!!.itemCount
if (cPosition < adapterCount) {
mAdapter!!.onBindViewHolder(holder, cPosition)
return
}
}
}
class HeadViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
class FootViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
companion object {
const val TYPE_HEADER = -1
const val TYPE_FOOTER = -2
const val TYPE_INVALID = -3
}
}
这样最后调用的时候直接调用addHeaderView
和addFooterView
即可添加View,如下:
private fun setSimpleAdapter(listData: ArrayList<TestData>) {
var headView1 = TestHeaderView(this)
var footView1 = TestHeaderView(this)
var testAdapter = TestAdapter(listData)
listView?.addHeaderView(headView1)
listView?.addFooterView(footView1)
listView?.setAdapter(testAdapter)
listView?.addItemDecoration(DividerItemDecoration(this,DividerItemDecoration.VERTICAL))
listView?.layoutManager = LinearLayoutManager(this)
}
效果如下图:
recyclerview添加头尾部.jpg总结
类比于ListView的添加头尾部思路,为Recyclerview设计添加头尾部,本质上还是通过不同的itemtype
实现不同的布局。
网友评论