普通方法
(使用起来非常简单,但是它会添加额外的布局,因此效率略低于第二种方法):
为header创建一个xml布局(可以包括任意view或者ViewGroup)
<FrameLayout
android:layout_width="match_parent"
android:layout_height="100dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="header"/>
</FrameLayout>
使用静态初始方法从xml中创建RecyclerViewHeader
RecyclerViewHeader header = RecyclerViewHeader.fromXml(context, R.layout.header)
将RecyclerViewHeader Attach 到RecyclerView,搞定!
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view)
// set LayoutManager for your RecyclerView
header.attachTo(recyclerView)
Header-already-aligned approach (不会引入任何额外布局):
将RecyclerViewHeader布局放在RecyclerView的上层。
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top" />
<com.bartoszlipinski.recyclerviewheader.RecyclerViewHeader
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="center_horizontal|top">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="header"/>
</com.bartoszlipinski.recyclerviewheader.RecyclerViewHeader>
</FrameLayout>
获得RecyclerViewHeader对象:
RecyclerViewHeader header = (RecyclerViewHeader) findViewById(R.id.header)
把RecyclerViewHeader赋予RecyclerView
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view)
// set LayoutManager for your RecyclerView
header.attachTo(recyclerView, true)
注意事项
RecyclerViewHeader必须在RecyclerView设置了LayoutManager之后调用。
目前该库适用于LinearLayoutManager,GridLayoutManager和StaggeredGridLayoutManager布局的RecyclerViews。
只支持垂直布局LayoutManager
RecyclerView 出来有很长一段时间了,相信大家对它已经很熟悉了,使用过它的朋友可能都会发现一点,就是 RecyclerView 不能添加 headerView 和 footView,这就让我们有点蛋疼了,也许你会说,没事啊,我们可以重写getItemViewType(int position)这个方法,让他实现多个布局,具体实现如下:@Override
public int getItemViewType(int position) {
if (position == 0) {
return HEAD_VIEW
}else{
return BODY_VIEW
}
}
123456789
下面的 *** 作就不多讲了,这种方法相信大家在使用 ListView 的时候就非常熟练; 那既然可以通过以上方法实现,为什么还要写一个可以添加 HeaderView 和 FootView 的 RecyclerView ?其实这个问题不用回答,因为 ListView 也可以通过以上方法实现,但它还是有 addHeaderView和 addFootView 的方法,不过话说回来,通过 addHeaderView 实现的添加头部布局确实有不可代替的功能,比如,ListView 头部图片下拉放大动画;在调用 adapter.notifyDataSetChanged()头部数据是不会重新刷新的等等。
好了,接下来才是本文的主题内容,如何实现 RecyclerView 添加 HeaderView 和 FootView?
无从下手?没关系!ListView 不是已经帮我们实现了这样的功能吗,我们可以先看看 ListView 是如何实现的,
public void addHeaderView(View v, Object data, boolean isSelectable) {
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)) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter)
}
// 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()
}
}
}
12345678910111213141516171819202122
以上方法核心代码就是
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter)1
其他可以忽略不看;这里其实用到了一种设计模式的概念,好像是叫什么装饰模式吧,意思就是把原有的 Adapter 通过HeaderViewListAdapter这个包装类重新包装一次,把改增的地方增一增,该减的地方减一点;所以我们要关注的是HeaderViewListAdapter类是如何实现的,然后再依葫芦画瓢画一个包装 RecyclerView.Adapter的包装类!
解密HeaderViewListAdapter
这个包装类代码本来就不长,相信大家都能够理解的,这里就挑点重点讲一下
1、对Adapter 的 count 进行了重新计算,这个不用多解释吧!
public int getCount() {
if (mAdapter != null) {
return getFootersCount() + getHeadersCount() + mAdapter.getCount()
} else {
return getFootersCount() + getHeadersCount()
}
}
12345678
2、就是getView方法,这个方法是重点,如果当前 position 的位置比 HeaderView 的数量小,那么返回的就是 HeaderView 的 对应的 View,否则再判断 原 Adapter 的 count 与当前 position 的差值来比较,是调用原 Adapter 的 getView 方法,还是获取 footView 的 view;说白了这个方法的目的就是添加了头部和尾部 View。
public View getView(int position, View convertView, ViewGroup parent) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount()
if (position <numHeaders) {
return mHeaderViewInfos.get(position).view
}
// Adapter
final int adjPosition = position - numHeaders
int adapterCount = 0
if (mAdapter != null) {
adapterCount = mAdapter.getCount()
if (adjPosition <adapterCount) {
return mAdapter.getView(adjPosition, convertView, parent)
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).view
}1234567891011121314151617181920
对于这个包装类的其他方法只要注意一下就 没问题了,那么接下来,就是我们模仿的时刻了!
代码非常简单,这里就直接上了
1、包装类 RecyclerWrapAdapter
package moon.myapplication
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
import java.util.ArrayList
/**
* Created by moon.zhong on 2015/7/20.
* time : 14:10
*/
public class RecyclerWrapAdapter extends RecyclerView.Adapter implements WrapperAdapter {
private RecyclerView.Adapter mAdapter
private ArrayList<View>mHeaderViews
private ArrayList<View>mFootViews
static final ArrayList<View>EMPTY_INFO_LIST =
new ArrayList<View>()
private int mCurrentPosition
public RecyclerWrapAdapter(ArrayList<View>mHeaderViews, ArrayList<View>mFootViews, RecyclerView.Adapter mAdapter) {
this.mAdapter = mAdapter
if (mHeaderViews == null) {
this.mHeaderViews = EMPTY_INFO_LIST
} else {
this.mHeaderViews = mHeaderViews
}
if (mHeaderViews == null) {
this.mFootViews = EMPTY_INFO_LIST
} else {
this.mFootViews = mFootViews
}
}
public int getHeadersCount() {
return mHeaderViews.size()
}
public int getFootersCount() {
return mFootViews.size()
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == RecyclerView.INVALID_TYPE) {
return new HeaderViewHolder(mHeaderViews.get(0))
} else if (viewType == RecyclerView.INVALID_TYPE - 1) {
return new HeaderViewHolder(mFootViews.get(0))
}
return mAdapter.onCreateViewHolder(parent, viewType)
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int numHeaders = getHeadersCount()
if (position <numHeaders) {
return
}
int adjPosition = position - numHeaders
int adapterCount = 0
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount()
if (adjPosition <adapterCount) {
mAdapter.onBindViewHolder(holder, adjPosition)
return
}
}
}
@Override
public int getItemCount() {
if (mAdapter != null) {
return getHeadersCount() + getFootersCount() + mAdapter.getItemCount()
} else {
return getHeadersCount() + getFootersCount()
}
}
@Override
public int getItemViewType(int position) {
mCurrentPosition = position
int numHeaders = getHeadersCount()
if (position <numHeaders) {
return RecyclerView.INVALID_TYPE
}
int adjPosition = position - numHeaders
int adapterCount = 0
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount()
if (adjPosition <adapterCount) {
return mAdapter.getItemViewType(adjPosition)
}
}
return RecyclerView.INVALID_TYPE - 1
}
@Override
public long getItemId(int position) {
int numHeaders = getHeadersCount()
if (mAdapter != null &&position >= numHeaders) {
int adjPosition = position - numHeaders
int adapterCount = mAdapter.getItemCount()
if (adjPosition <adapterCount) {
return mAdapter.getItemId(adjPosition)
}
}
return -1
}
@Override
public RecyclerView.Adapter getWrappedAdapter() {
return mAdapter
}
private static class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView)
}
}
}
2、为 RecyclerView 添加 AddHeaderView 和 addFootView 方法,重写 RecyclerView
package moon.myapplication
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.util.AttributeSet
import android.view.View
import java.util.ArrayList
/**
* Created by moon.zhong on 2015/7/20.
* time : 15:14
*/
public class WrapRecyclerView extends RecyclerView {
private ArrayList<View>mHeaderViews = new ArrayList<>()
private ArrayList<View>mFootViews = new ArrayList<>()
private Adapter mAdapter
public WrapRecyclerView(Context context) {
super(context)
}
public WrapRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs)
}
public WrapRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle)
}
public void addHeaderView(View view){
mHeaderViews.clear()
mHeaderViews.add(view)
if (mAdapter != null){
if (!(mAdapter instanceof RecyclerWrapAdapter)){
mAdapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter)
//mAdapter.notifyDataSetChanged()
}
}
}
public void addFootView(View view){
mFootViews.clear()
mFootViews.add(view)
if (mAdapter != null){
if (!(mAdapter instanceof RecyclerWrapAdapter)){
mAdapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter)
//mAdapter.notifyDataSetChanged()
}
}
}
@Override
public void setAdapter(Adapter adapter) {
if (mHeaderViews.isEmpty()&&mFootViews.isEmpty()){
super.setAdapter(adapter)
}else {
adapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,adapter)
super.setAdapter(adapter)
}
mAdapter = adapter
}
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
到这里就实现了 RecyclerView 添加头部和尾部布局 ,用法很简单,其他步骤都不变,只需把 RecyclerView 换成WrapRecyclerView即可。
看看效果图
这篇文章没有什么创新的技术点,但却给广大程序员一个很好的思路:那就是模仿,最好是能自己动手,也许你看一遍就能理解,但是真正动手实践的时候你会发现很多问题!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)