Android RecyclerView使用详解四
来源:互联网 发布:新疆人 知乎 编辑:程序博客网 时间:2024/04/30 05:51
转载请注明:
http://blog.csdn.net/sinat_30276961/article/details/50365513
上一篇介绍了RecyclerView自定义分隔图的代码绘制方式,本篇将在上篇的基础上讲解RecyclerView的header和footer的添加和设定,并且讲完header和footer,RecyclerView的基础内容就讲完了。
ok,废话少说。我们都知道,在ListView里添加header和footer是一件再简单不过的事情,秒秒钟搞定。那么在RecyclerView里添加呢?
在前面几篇里,我已经讲过了,RecyclerView的大部分功能都分离出来了,需要我们自己去复写,然后添加进去。虽然增加了麻烦度,但是带来的是灵活和无限的创造性。所以,header和footer的添加也是需要我们自己去代码设定加进去的。
我们再回忆一下RecyclerView创建需要的几个核心:
1. LayoutManager(必须)
2. ItemDecoration (非必须)
3. Adapter(必须)
4. ItemAnimator (非必须)
那么,header和footer该怎么加进去呢?
我们先来回忆一下RecyclerView.Adapter里包含的接口:
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); public abstract void onBindViewHolder(VH holder, int position); public int getItemViewType(int position) { return 0; } public void setHasStableIds(boolean hasStableIds) { if (hasObservers()) { throw new IllegalStateException("Cannot change whether this adapter has " + "stable IDs while the adapter has registered observers."); } mHasStableIds = hasStableIds; } public long getItemId(int position) { return NO_ID; } public abstract int getItemCount(); public void onViewRecycled(VH holder) { } public boolean onFailedToRecycleView(VH holder) { return false; } public void onViewAttachedToWindow(VH holder) { } public void onViewDetachedFromWindow(VH holder) { } public void registerAdapterDataObserver(AdapterDataObserver observer) { mObservable.registerObserver(observer); } public void unregisterAdapterDataObserver(AdapterDataObserver observer) { mObservable.unregisterObserver(observer); } public void onAttachedToRecyclerView(RecyclerView recyclerView) { } public void onDetachedFromRecyclerView(RecyclerView recyclerView) { }
我把final的接口都摒弃掉了,上述接口是可以去复写的。从这些接口看,我们可以发现,一般情况,我们需要复写的接口不多,大致是如下几个:
1. onCreateViewHolder
2. onBindViewHolder
3. getItemCount
4. getItemId
当然,如果你需要实现复杂的功能,可能还需要如下接口:
1. getItemViewType(定义当前item的类型)
2. onViewRecycled(当前item被回收时调用,可用来释放绑定在view上的大数据,比方说Bitmap)
3. ……(太多。。)
常用的接口,在前面几篇里已经都讲解过,这里重点讲下getItemViewType。
为啥要说这个接口呢?其实要实现在RecyclerView里添加headerView和footerView,这个接口是必不可少的。
getItemViewType是用来自己设定不同item的类型。既然如此,我们可以把header和footer看成是不同于一般item的类型不就行了。然后根据不同的viewType,我创建不同的view,也即是说,还需要修改
1. onCreateViewHolder(创建不同view)
2. onBindViewHolder(根据不同view,绑定不同数据)
3. getItemCount(header和footer不应该在此数量里)
4. getItemId(header占了index=0这个位置,所以需要调整)
ok,基本思路有了,现在想下方案。根据上述理念,我们目前能很容易想到的有两套方案:
1. 在原先自己写的adapter里根据需要添加上header和footer。
2. 通过装饰模式,在新的adapter里添加header和footer。
第一个方案,可行。
But!
要修改原来的代码,而且新改出来的adapter复用性差。
第二个方案,可行。
And!
不需要改动原来的adapter,复用性大大的提高。
鉴于此,我选择第二个方案(其实,你去看ListView的代码,你会发现,它添加header和footer的方式也是通过装饰模式的)。先来看看,我实现的添加headerView和footerView之后的效果:
这里添加的header和footer很简单,就是一个TextView。
ok,接下去,就是代码部分。我先贴出Adapter的代码:
public class HeaderRecyclerViewAdapter extends RecyclerView.Adapter{ public static final int ITEM_VIEW_TYPE_HEADER = -2; public static final int ITEM_VIEW_TYPE_FOOTER = -3; public static final int ITEM_VIEW_TYPE_ITEM = 0; private RecyclerView.Adapter mAdapter; private View mHeaderView; private View mFooterView; public HeaderRecyclerViewAdapter(View headerView, View footerView, RecyclerView.Adapter adapter) { mHeaderView = headerView; mFooterView = footerView; mAdapter = adapter; } public RecyclerView.Adapter getWrappedAdapter() { return mAdapter; } public boolean hasHeaderView() { return mHeaderView != null; } public boolean hasFooterView() { return mFooterView != null; } public int headerViewHeight() { int height = 0; if (mHeaderView != null) { height = mHeaderView.getLayoutParams().height; } return height; } public int footerViewHeight() { int height = 0; if (mFooterView != null) { height = mFooterView.getLayoutParams().height; } return height; } @Override public int getItemViewType(int position) { if (mHeaderView != null) { if (position == 0) { return ITEM_VIEW_TYPE_HEADER; } else { final int adjPosition = position - 1; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { return ITEM_VIEW_TYPE_ITEM; } } } } else { int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (position < adapterCount) { return ITEM_VIEW_TYPE_ITEM; } } } return ITEM_VIEW_TYPE_FOOTER; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == ITEM_VIEW_TYPE_HEADER) { return new HeaderViewHolder(mHeaderView); } else if (viewType == ITEM_VIEW_TYPE_FOOTER) { return new HeaderViewHolder(mFooterView); } else { return mAdapter.onCreateViewHolder(parent, viewType); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { final int viewType = getItemViewType(position); if (viewType == ITEM_VIEW_TYPE_HEADER || viewType == ITEM_VIEW_TYPE_FOOTER) { return; } else { int adjPosition = position; if (mHeaderView != null) { adjPosition--; } mAdapter.onBindViewHolder(holder, adjPosition); } } @Override public int getItemCount() { if (mAdapter != null) { return (mHeaderView == null ? 0 : 1) + (mFooterView == null ? 0 : 1) + mAdapter.getItemCount(); } else { return (mHeaderView == null ? 0 : 1) + (mFooterView == null ? 0 : 1); } } @Override public long getItemId(int position) { final boolean hasHeader = (mHeaderView != null); final boolean hasFooter = (mFooterView != null); int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); } if (hasHeader) { if (position < adapterCount + 1) { return position - 1; } else { return ITEM_VIEW_TYPE_FOOTER; } } else { if (position < adapterCount) { return position; } else { return ITEM_VIEW_TYPE_FOOTER; } } } static class HeaderViewHolder extends RecyclerView.ViewHolder { public HeaderViewHolder(View itemView) { super(itemView); } }
这部分代码其实不复杂,只要思路理清了,实现起来不麻烦。
第2、3、4行,定义了几种viewType。可以看到header我是从-2开始的。这是因为-1是无效的item(INVALID_TYPE = -1)。当然,你也可以从1开始定义。
第6行的mAdapter就是需要包装的原始adapter。
第8、9行,我定义了一个headerView和footerView。在ListView里,可以添加多个headerView和footerView,这里,我就只实现添加一个header和footer。如果要和ListView一样,那就把View改为List< View >。
第46-71行,复写了getItemViewType。在原始的position==0的位置,用来放置header;最后一个位置,用来放置footer。
第74-82行,复写了onCreateViewHolder。根据不同的viewType,我创建不同的view和viewholder。
第85-96行,复写了onBindViewHolder。如果viewType是header或者footer,就直接return。如果是一般的item,还需要调整下它的position。如果有header,原先的position还要-1。这样一来,在被装饰的原始adapter里,得到的position就已经排除了header的占位。
后面的代码和上述差不多,就是把header和footer的情况特殊处理,这里就不讲了。
接着,我贴下RecyclerView复写的代码:
public class HeaderRecyclerView extends RecyclerView{ private View mHeaderView; private View mFooterView; public HeaderRecyclerView(Context context) { super(context); } public HeaderRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public HeaderRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void addHeaderView(View headerView) { mHeaderView = headerView; } public void addFooterView(View footerView) { mFooterView = footerView; } public void setAdapter(Adapter adapter) { if (mHeaderView == null && mFooterView == null) { super.setAdapter(adapter); } else { adapter = new HeaderRecyclerViewAdapter(mHeaderView, mFooterView, adapter); super.setAdapter(adapter); } } public void removeHeaderView() { mHeaderView = null; } public void removeFooterView() { mFooterView = null; }}
上述代码就复写了setAdapter,代码很简单,不讲了。
接着是额外的部分,我把分隔图也重新改了一下,为了适应多了个header和footer。
先是HeaderLinearLayoutItemDecoration:
public class HeaderLinearLayoutItemDecoration extends RecyclerView.ItemDecoration{ final Context mContext; final int mOrientation; final Drawable mDividerDrawable; int mDividerHeight; private static final int[] ATTRS = new int[]{ android.R.attr.listDivider, android.R.attr.dividerHeight }; public HeaderLinearLayoutItemDecoration(Context context, int orientation) { mContext = context; mOrientation = orientation; TypedArray ta = context.obtainStyledAttributes(ATTRS); mDividerDrawable = ta.getDrawable(0); mDividerHeight = ta.getDimensionPixelSize(1, 1); ta.recycle(); } public HeaderLinearLayoutItemDecoration(Context context, int orientation, int dividerHeight) { mOrientation = orientation; mContext = context; mDividerHeight = dividerHeight; TypedArray ta = context.obtainStyledAttributes(ATTRS); mDividerDrawable = ta.getDrawable(0); ta.recycle(); } public HeaderLinearLayoutItemDecoration(Context context, int orientation, Drawable dividerDrawable, int dividerHeight) { mContext = context; mOrientation = orientation; mDividerDrawable = dividerDrawable; mDividerHeight = dividerHeight; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if (mOrientation == LinearLayoutManager.HORIZONTAL) { drawHorizontal(c, parent); } else { drawVertical(c, parent); } } private void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight()-parent.getPaddingBottom(); final int count = parent.getChildCount(); for (int i = 0; i < count; i++) { final View child = parent.getChildAt(i); final int position = parent.getChildAdapterPosition(child); final int viewType = parent.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { continue; } final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int left = child.getRight()+params.rightMargin; final int right = left+mDividerHeight; mDividerDrawable.setBounds(left, top, right, bottom); mDividerDrawable.draw(c); } } private void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth()-parent.getPaddingRight(); final int count = parent.getChildCount(); for (int i = 0; i < count; i++) { final View child = parent.getChildAt(i); final int position = parent.getChildAdapterPosition(child); final int viewType = parent.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { continue; } final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int top = child.getBottom()+params.bottomMargin; final int bottom = top + mDividerHeight; mDividerDrawable.setBounds(left, top, right, bottom); mDividerDrawable.draw(c); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { final int position = parent.getChildAdapterPosition(view); final int viewType = parent.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { return; } if (mOrientation == LinearLayoutManager.VERTICAL) { outRect.bottom = mDividerHeight; } else { outRect.right = mDividerHeight; } }}
代码其实和原先的LinearLayoutItemDecoration几乎一样,唯一的区别在于:我在drawHorizontal、drawVertical和getItemOffsets里在遍历child时,多加了个判断,如果是header或者footer,就不画不偏移。
再贴出HeaderGridLayoutItemDecoration:
public class HeaderGridLayoutItemDecoration extends RecyclerView.ItemDecoration{ final Context mContext; final Drawable mDividerDrawable; int mDividerHeight; private static final int[] ATTRS = new int[] { android.R.attr.listDivider, android.R.attr.dividerHeight }; public HeaderGridLayoutItemDecoration(Context context) { mContext = context; // 从主题去获取属性键值 TypedArray ta = context.obtainStyledAttributes(ATTRS); mDividerDrawable = ta.getDrawable(0); mDividerHeight = ta.getDimensionPixelSize(1, 1); ta.recycle(); } public HeaderGridLayoutItemDecoration(Context context, int height) { mContext = context; // 从主题去获取属性键值 TypedArray ta = context.obtainStyledAttributes(ATTRS); mDividerDrawable = ta.getDrawable(0); mDividerHeight = height; ta.recycle(); } public HeaderGridLayoutItemDecoration(Context context, Drawable drawable) { mContext = context; mDividerDrawable = drawable; TypedArray ta = context.obtainStyledAttributes(ATTRS); mDividerHeight = ta.getDimensionPixelSize(1, 1); ta.recycle(); } public HeaderGridLayoutItemDecoration(Context context, Drawable drawable, int height) { mContext = context; mDividerDrawable = drawable; mDividerHeight = height; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { final GridLayoutManager manager = (GridLayoutManager) parent.getLayoutManager(); final int spanCount = manager.getSpanCount(); drawHorizontal(c, parent, state, spanCount); drawVertical(c, parent, state, spanCount); } private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state, final int spanCount) { final int count = parent.getChildCount(); // 确定有几行 final int rowCount = count/spanCount + (count%spanCount==0?0:1); final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); for (int i = 0; i < rowCount; i++) { final View child = parent.getChildAt(i*spanCount); final int position = parent.getChildAdapterPosition(child); final int viewType = parent.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { continue; } final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDividerHeight; mDividerDrawable.setBounds(left, top, right, bottom); mDividerDrawable.draw(c); } } private void drawVertical(Canvas c, RecyclerView parent, RecyclerView.State state, final int spanCount) { final int count = parent.getChildCount(); for (int i = 0; i < count; i++) { final View child = parent.getChildAt(i); final int position = parent.getChildAdapterPosition(child); final int viewType = parent.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { continue; } final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + mDividerHeight; final int top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin + mDividerHeight; mDividerDrawable.setBounds(left, top, right, bottom); mDividerDrawable.draw(c); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); final GridLayoutManager manager = (GridLayoutManager) parent.getLayoutManager(); final int spanCount = manager.getSpanCount(); final int position = parent.getChildAdapterPosition(view); final int adjPosition = (int) parent.getAdapter().getItemId(position); final int count = parent.getAdapter().getItemCount(); final int viewType = parent.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { return; } if ((adjPosition == count -1) && (adjPosition % spanCount) == (spanCount - 1)) { // 最后一个,如果也是最右边,那么就不需要偏移 } else if (adjPosition >= (count - (count % spanCount))) { // 最下面一行,只要右边偏移就行 outRect.right = mDividerHeight; } else if ((adjPosition % spanCount) == (spanCount - 1)) { // 最右边一列,只要下面偏移就行 outRect.bottom = mDividerHeight; } else { // 其他的话,右边和下面都要偏移 outRect.set(0, 0, mDividerHeight, mDividerHeight); } }}
这里,思路和HeaderLinearLayoutItemDecoration一样,就是在画和设置偏移的地方多加个header和footer的判断。
唯一不同的是,drawVertical和GridLayoutItemDecoration里的drawVertical不一样了。因为原先的实现方式是遍历span数量,并画spanCount数量的垂直drawable,并且是从屏幕最上方到屏幕最下方(parent.getPaddingTop和parent.getHeight() - parent.getPaddingBottom())。但是如果添加了header和footer,原先的方案就会使垂直drawable覆盖在header和footer上。并且如果是通过设置header和footer高度的偏移值来去掉header和footer那里的垂直drawable,也不好判断当前滑动位置是否包含header和footer,包含多少。所以,最好的方案是一个个item遍历画。
ok,到目前为止,所有需要的角色都已经讲完了。接着就是测试代码了:
public class AddHeaderViewTest extends ActionBarActivity { HeaderRecyclerView mHeaderRecyclerView; RecyclerViewAdapter mLinearLayoutAdapter; HeaderLinearLayoutItemDecoration mLinearLayoutItemDecoration; LinearLayoutManager mLinearLayoutManager; GridRecyclerViewAdapter mGridLayoutAdapter; HeaderGridLayoutItemDecoration mGridLayoutItemDecoration; GridLayoutManager mGridLayoutManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_header_view_test); mHeaderRecyclerView = (HeaderRecyclerView) findViewById(R.id.headerRecyclerView); mLinearLayoutManager = new LinearLayoutManager(this); mGridLayoutManager = new GridLayoutManager(this, 4); // grid类型,需要复写这个方法 // 确定每个item的横跨span数 mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { final int viewType = mHeaderRecyclerView.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { return mGridLayoutManager.getSpanCount(); } else { return 1; } } }); mHeaderRecyclerView.setLayoutManager(mLinearLayoutManager); mLinearLayoutItemDecoration = new HeaderLinearLayoutItemDecoration(this, LinearLayoutManager.VERTICAL, 4); mGridLayoutItemDecoration = new HeaderGridLayoutItemDecoration(this, 18); mHeaderRecyclerView.addItemDecoration(mLinearLayoutItemDecoration); // 添加header TextView headerView = new TextView(this); headerView.setText("Header"); headerView.setTextColor(Color.GRAY); headerView.setTextSize(18); headerView.setGravity(Gravity.CENTER); ViewGroup.LayoutParams headerParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, DensityUtil.dpToPx(60, getResources()) ); headerView.setLayoutParams(headerParams); mHeaderRecyclerView.addHeaderView(headerView); // 添加footer TextView footerView = new TextView(this); footerView.setText("Footer"); footerView.setTextColor(Color.GRAY); footerView.setTextSize(18); footerView.setGravity(Gravity.CENTER); ViewGroup.LayoutParams footerParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, DensityUtil.dpToPx(60, getResources()) ); footerView.setLayoutParams(footerParams); mHeaderRecyclerView.addFooterView(footerView); mLinearLayoutAdapter = new RecyclerViewAdapter(this, R.layout.item_normal_recycler_view, 50); mGridLayoutAdapter = new GridRecyclerViewAdapter(this, R.layout.item_recycler_view_grid, 100); mLinearLayoutAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(AddHeaderViewTest.this, "The " + position + " click", Toast.LENGTH_SHORT).show(); } }); mGridLayoutAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(AddHeaderViewTest.this, "The " + position + " click", Toast.LENGTH_SHORT).show(); } }); mHeaderRecyclerView.setAdapter(mLinearLayoutAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_add_header_view_test, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_switch) { if (mHeaderRecyclerView.getLayoutManager() == mLinearLayoutManager) { mHeaderRecyclerView.setLayoutManager(mGridLayoutManager); mHeaderRecyclerView.removeItemDecoration(mLinearLayoutItemDecoration); mHeaderRecyclerView.addItemDecoration(mGridLayoutItemDecoration); mHeaderRecyclerView.setAdapter(mGridLayoutAdapter); } else { mHeaderRecyclerView.setLayoutManager(mLinearLayoutManager); mHeaderRecyclerView.removeItemDecoration(mGridLayoutItemDecoration); mHeaderRecyclerView.addItemDecoration(mLinearLayoutItemDecoration); mHeaderRecyclerView.setAdapter(mLinearLayoutAdapter); } return true; } return super.onOptionsItemSelected(item); }}
这部分代码和原先的差不多,唯一不同点在于布局文件里的RecyclerView要使用自定义的HeaderRecyclerView。
还有一个地方是:
// grid类型,需要复写这个方法 // 确定每个item的横跨span数 mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { final int viewType = mHeaderRecyclerView.getAdapter().getItemViewType(position); if (viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_HEADER || viewType == HeaderRecyclerViewAdapter.ITEM_VIEW_TYPE_FOOTER) { return mGridLayoutManager.getSpanCount(); } else { return 1; } } });
对于GridLayoutManager,你需要通过setSpanSizeLookup复写getSpanSize这个接口。这个是用来确定每个item占据的span数量。默认情况下,返回的是1,也就是每个item占据1个span。
对于header和footer这两个item,每个都需要占据spanCount数量的span。
好了,关于RecyclerView添加headerView和footerView部分讲解完了。并且,RecyclerView的基础部分都讲解完了。
Have a good night!
- Android RecyclerView使用详解四
- Android RecyclerView 使用详解
- Android RecyclerView使用详解
- Android RecyclerView使用详解
- Android RecyclerView使用详解
- Android RecyclerView使用详解
- Android RecyclerView使用详解
- Android RecyclerView 详解(四) RecyclerView分割线的使用和分割线样式
- Android RecyclerView使用详解一
- Android RecyclerView使用详解二
- Android RecyclerView使用详解三
- android RecyclerView使用及详解
- Android RecyclerView 详解(五) RecyclerView多布局的使用
- Android RecyclerView使用详解(一)
- Android RecyclerView使用详解(二)
- Android RecyclerView使用详解(三)
- Android 中RecyclerView使用详解(一)
- android RecyclerView 瀑布流使用详解
- 在对话框和widget窗口添加菜单
- Dalvik switch语句
- karma requirejs 加载问题小记
- PHP中final类,抽象类,接口结构
- 编写高质量iOS 与OSX 代码的52个有效方法之系统框架
- Android RecyclerView使用详解四
- Two Sum LeetCode OJ
- poj 3281 网络流最大流量
- 专题:如何写测试——Spark
- Null & nil & Nil & NSNull
- XML应用于处理
- pydev的注释快捷键
- centos7 yum install nginx
- 工作流