RecyclerView的痛点使用
2016-04-08 11:59
459 查看
前言
申明:这里不提供封装好的RecylerView库,碍于作者能力,只能提供一些思路和想法。同时,这不是一篇基础性的RecylerView的使用文章,文章中我将主要对我本人在使用过程遇到的通点进行归纳,并提供自己的解决方法。众所周知,在Android 5.0之后,Google引入Material Design设计,同时为我们带来了ListView的替代品RecylerView。对比ListView, RecyclerView在可扩展性,灵活性上更方便我们自己去定制。话不多说,下面我将从下拉刷新、上拉加载更多、自定义分割线、item点击事件阐述我的跟人想法。
下拉刷新
本着用最简便的方式实现需求的原则,我们在下拉刷新这块主要是借助Google提供的SwipeRefreshLayout控件。SwipeRefreshLayout使用起来很简单,代码如下:
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/refresh" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:paddingLeft="16dp" android:paddingRight="16dp" android:paddingTop="16dp" /> </android.support.v4.widget.SwipeRefreshLayout>
实现下拉刷新,只需要调用SwipeRefreshLayout的监听事件OnRefreshListener,在监听事件方法里实现响应的逻辑就行了
mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { new Handler().postDelayed(new Runnable() { @Override public void run() { texts.clear(); i = 0; setData(); mAdapter.notifyDataSetChanged(); mRefresh.setRefreshing(false); } },2000); } });
自定义分割线
使用RecyclerView时,为了控制item之间的距离,我们既可以通过设置item的margin调整item之间的距离,也可以调用RecyclerView的addItemDecoration()方法设置分割线来控制item之间的距离。这里我们使用的是后者。我们需要自定义一个分割线类继承自RecyclerView.ItemDecoration,重写onDraw()方法(绘制分割线)以及getItemOffsets()方法(设置分割线的大小)。
此处给出我的自定义分割线源码
public class DividerItemDecoration extends RecyclerView.ItemDecoration { private int orientation; //转换成整型后分割线尺寸 private int itemSize; //待传入的分割线尺寸,如果没有传入,初始化的时候默认16 private float size; private Paint mPaint; private Context context; public DividerItemDecoration(Context context) { this(context, 16, LinearLayoutManager.VERTICAL); } public DividerItemDecoration(Context context, float size) { this(context, size, LinearLayoutManager.VERTICAL); } public DividerItemDecoration(Context context, int orientation) { this(context, 16, orientation); } public DividerItemDecoration(Context context, float size, int orientation) { if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) { throw new IllegalArgumentException("the orientation is not VERTICAL or HORIZONTAL!"); } this.context = context; this.size = size; this.orientation = orientation; initAttrs(); } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if (orientation == LinearLayoutManager.VERTICAL) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (orientation == LinearLayoutManager.VERTICAL) { outRect.set(0, 0, 0, itemSize); } else { outRect.set(0, 0, itemSize, 0); } } private void initAttrs() { itemSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, context.getResources().getDisplayMetrics()); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.TRANSPARENT); mPaint.setStyle(Paint.Style.FILL); } /** * 绘制横向item分割线 * * @param canvas 画布 * @param parent recyclerView */ private void drawVertical(Canvas canvas, RecyclerView parent) { int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); int top = child.getBottom() + params.bottomMargin; int bottom = top + itemSize; canvas.drawRect(left, top, right, bottom, mPaint); } } /** * 绘制横向item分割线 * * @param canvas 画布 * @param parent recyclerView */ public void drawHorizontal(Canvas canvas, RecyclerView parent) { int top = parent.getPaddingTop(); int bottom = parent.getHeight() - parent.getPaddingBottom(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); int left = child.getRight() + params.rightMargin; int right = left + itemSize; canvas.drawRect(left, top, right, bottom, mPaint); afae } } }
item点击事件
RecyclerView本身没有给我们提供类似ListView的setOnItemClickListener的监听事件,所以我们只能自己去实现这样一个监听事件。在我们定义的Adapter中定义一个OnItemClickListener接口,接口中定义一个OnItemClick()方法,之后我们需要对外提供一个方法去实现这个接口。
public interface OnItemClickListener { void onItemClick(View view, int position); } private OnItemClickListener onItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; }
然后在onBindViewHolder中添加如下代码:
if (onItemClickListener != null) { ((ItemViewHolder) holder).itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onItemClickListener.onItemClick(((ItemViewHolder) holder).itemView, holder.getAdapterPosition()); } }); }
最后,如果需要给item实现点击事件,利用adapter.setOnItemClickListener()实现就可以了
mAdapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "click " + (position + 1), Toast.LENGTH_SHORT).show(); } });
上拉加载更多
上拉加载更多这块,主要是给RecyclerView添加一个底部布局。RecyclerView.Adapter给我们提供了getItemViewType方法来告诉我们每个item是属于正常布局还是属于底部布局@Override public int getItemViewType(int position) { if (position == texts.size()) { return FOOT_TYPE;//说明该item是底部 } return NORMAL_TYPE; }
同时为了实现加载数据,我们通过调用RecyclerView的addOnScrollListener()方法,实现onScrolled()方法和onScrollStateChanged()方法。
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { int itemLastIndex = mAdapter.getItemCount() - 1;//recyclerView最后一项位置 if (newState == RecyclerView.SCROLL_STATE_IDLE && visibleLastIndex == itemLastIndex) { if (i < 100) { mAdapter.setLoading(true); mAdapter.notifyDataSetChanged(); new Handler().postDelayed(new Runnable() { @Override public void run() { setData(); mAdapter.setLoading(false); mAdapter.notifyDataSetChanged(); } }, 2000); } else { mAdapter.setAll(true); mAdapter.notifyDataSetChanged(); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); visibleLastIndex = manager.findLastCompletelyVisibleItemPosition(); } });
简单说下思路,调用此监听事件后,当RecyclerView在滑动过程中会不断回调一些数据来表示当前RecyclerView的状态。我们为了实现上拉加载更多,我们需要知道,当RecyclerView滑到底部时,同时继续向上滑动我们就需要加载更多数据,所以在onScrolled()方法中我们利用findLastCompleteVisibleItemPosition()方法来获得当前RecyclerView停止滑动时最后一个可见项的位置是多少,当RecyclerView在滑动状态改变时,我们判断如果RecylerView改变的状态是停止滑动同时当前可见项位置是Adapter除底部项外最后一个,那么这个时候我们就需要加载数据。
注
关于自定义分割线的部分主要参见该博客http://blog.csdn.net/jxxfzgy/article/details/43736385由于作者能力有限,文章若有不足或者错误之处望指正交流
项目的Github地址:https://github.com/ReedGit/RecyclerViewDemo
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories