1.RecyclerView的基本使用方法:
RecyclerView:
A flexible view for providing a limited window into a large data set.
就是说RecyclerView可以像ListView,GridView一样展示大数据集合的组件。
使用方式当然也和ListView 相似,但是要比ListView稍微复杂一点点,因为他的可扩展性更强。
1. 需要在build.gridle中的dependencied块中添加依赖
compile 'com.android.support:recyclerview-v7:25.0.0'
2. 然后就可以在布局中引入RecyclerView了
<android.support.v7.widget.RecyclerView
android:id="@+id/id_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
3. 声明FirstAdapter类继承RecyclerView.Adapter
public class FirstAdapter extends RecyclerView.Adapter<FirstAdapter.FirstViewHolder> {
private List<String> mDataList;
public FirstAdapter() {
this(null);
}
public FirstAdapter(List<String> dataList) {
this.mDataList = mDataList == null ? new ArrayList<String>() : dataList;
}
/**
* 向RecyclerView填充数据
*
* @param dataList 数据集合
*/
public void setData(List<String> dataList) {
if (dataList == null)
return;
this.mDataList = dataList;
notifyDataSetChanged();
}
//顾名思义,这个方法要返回一个ViewHolder对象
@Override
public FirstViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout_recycler_main, null);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) convertView.getLayoutParams();
if (params == null) {
params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
convertView.setLayoutParams(params);
return new FirstViewHolder(convertView);
}
//绑定数据
@Override
public void onBindViewHolder(FirstViewHolder holder, int position) {
holder.id_tv_name.setText(mDataList.get(position));
}
@Override
public int getItemCount() {
return mDataList.size();
}
//声明内部类FirstViewHolder
static class FirstViewHolder extends RecyclerView.ViewHolder {
TextView id_tv_name;
public FirstViewHolder(View itemView) {
super(itemView);
id_tv_name = (TextView) itemView.findViewById(R.id.id_tv_name);
}
}
}
4. 在Activity使用
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private FirstAdapter mAdapter;
private ItemTouchHelper mItemTouchHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initData() {
List<String> dataList=new ArrayList<>();
for (int i = ; i < ; i++) {
dataList.add("---->"+i);
}
mAdapter.setData(dataList);
}
private void initView() {
mRecyclerView= (RecyclerView) findViewById(R.id.id_recycler_view);
//确保RecyclerView的尺寸是个常数,可以查看源码,false的时候代价很大。
mRecyclerView.setHasFixedSize(true);
//设置RecyclerView的布局管理器,这里使用线性布局管理器LinearLayoutManager
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//设置RecyclerView的Item动画,这里使用默认动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//绑定Adapter
mAdapter=new FirstAdapter();
mRecyclerView.setAdapter(mAdapter);
}
}
至此,就可以正常显示数据了,但是会发现没有分割线。这个随后再说,我们先来设置RecyclerView 的点击事件.
2.RecyclerView的点击事件:
我们要设置RecyclerView 的点击事件,发现它并没有类似ListView,GridView的setOnItemClickListener,那么怎么办呢?
一种方法是,我们可以在Adapter中声明一个接口,利用接口回调的方法事件itemView的点击事件。原来我一直都用的这种方法,感觉并不好用,毕竟咱们需要的是RecyclerView的点击事件,设置在Adapter中好像有点变味儿。
第二种方法是通过设置RecyclerView的addOnItemTouchListener,利用手势检测GestureDetectorCompat实现的,这是刚从一个博客中看到的,感觉很不错,就在这里记录一下。
步骤:
1. 声明一个抽象类实现RecyclerView的OnItemTouchListener接口,并重写其中的方法,需要在构造方法中传入RecyclerView
2. 定义抽象方法OnItemClick,OnItemLongClick分别表示单击事件和长按事件。
3. 该类需要持有GestureDetectorCompat对象,所有的触摸事件都交给GestureDetectorCompat处理,因此需要声明一个内部类OnItemTouchHelperGestureListener继承SimpleOnGestureListener,并实现onSingleTapUp(单击事件),onLongPress(长按事件)方法。
4. 在Activity中直接调用RecyclerView的addOnItemTouchListener即可使用
具体代码如下
/**
* 类功能描述:实现RecyclerView 的单击和长按时间
* 1.该类需要持有当前RecyclerView对象
* 2.该类需要持有GestureDetectorCompat对象,方便进行手势检测
*/
public abstract class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private GestureDetectorCompat mGestureDetectorCompat;
private RecyclerView mRecyclerView;
/**构造方法
* 1.传入RecyclerView
* 2.创建手势检测器对象
* @param mRecyclerView
*/
public OnRecyclerItemClickListener(RecyclerView mRecyclerView) {
this.mRecyclerView = mRecyclerView;
mGestureDetectorCompat = new GestureDetectorCompat(mRecyclerView.getContext(), new OnItemTouchHelperGestureListener());
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
//交给手势检测器处理
mGestureDetectorCompat.onTouchEvent(e);
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
//交给手势检测器处理
mGestureDetectorCompat.onTouchEvent(e);
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
//不用管
}
/**单击事件
* @param viewHolder viewholder
*/
public abstract void OnItemClick(RecyclerView.ViewHolder viewHolder);
/**长按事件
* @param viewHolder
*/
public abstract void OnItemLongClick(RecyclerView.ViewHolder viewHolder);
/**
* 手势检测器实现类,该类实现了单击和长按事件
*/
class OnItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
/**该方法会在点击弹起手指的时候调用,可以用来处理单击操作
* @param e
* @return
*/
@Override
public boolean onSingleTapUp(MotionEvent e) {
View childView = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (childView != null) {
RecyclerView.ViewHolder viewHolder = mRecyclerView.getChildViewHolder(childView);
OnItemClick(viewHolder);
}
return true;
}
/**长按操作
* @param e
*/
@Override
public void onLongPress(MotionEvent e) {
View childView = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (childView != null) {
RecyclerView.ViewHolder viewHolder = mRecyclerView.getChildViewHolder(childView);
OnItemLongClick(viewHolder);
}
}
}
}
具体调用:
mRecyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(mRecyclerView){
@Override
public void OnItemClick(RecyclerView.ViewHolder viewHolder) {
int position=viewHolder.getLayoutPosition();
Toast.makeText(MainActivity.this,"--->单击:"+mAdapter.getItem(position),Toast.LENGTH_SHORT).show();
}
@Override
public void OnItemLongClick(RecyclerView.ViewHolder viewHolder) {
int position=viewHolder.getLayoutPosition();
Toast.makeText(MainActivity.this,"--->长按:"+mAdapter.getItem(position),Toast.LENGTH_SHORT).show();
if (position!=){
mItemTouchHelper.startDrag(viewHolder);
}
}
});
这样的使用方法很像ListView了是吧。我们直接在View上设置View的监听事件,而不是通过adapter去设置,起码在理解上我觉得更简单了。

3.RecyclerView的分割线:
RecyclerView不能直接在xml中通过divider属性设置分割线,只能在代码中通过addItemDecoration方法去设置,该方法需要传入一个 ItemDecoration 对象
public void addItemDecoration(ItemDecoration decor) {}
系统并没有给我们提供默认的分割线实现类。一切都需要我们自己去做。
自定义分割线的步骤:
1. 声明分割线类DefaultDecoration,并重写getItemOffsets()方法
public class DefaultDecoration extends RecyclerView.ItemDecoration {
public DefaultDecoration() {
super();
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
/**
* 此方法会在ItemView布局之前调用
*
* @param outRect 可以理解为ItemView外边一层的一个Rect
* @param view 当前ItemView
* @param parent 当前的RecyclerView
* @param state RecyclerView的状态
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getChildAdapterPosition(view) != ) {
outRect.top = ;
}
}
}
这是最简单的一种情况,需要给RecyclerView设置一个背景色,分割线的颜色就是RecyclerView的背景色。如果想让分割显得颜色区别于背景色,这个方法是没法实现的。
下面介绍第二种方法。
第二种方法:
public class ColorDividerItemDecoration extends RecyclerView.ItemDecoration {
// 分割线的高度
private float mDividerHeight;
//画壁
private Paint mPaint;
public ColorDividerItemDecoration() {
//初始化画笔
mPaint=new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLUE);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount=parent.getChildCount();
//遍历所有的child,给每一个child都画上分割线
for (int i = ; i < childCount; i++) {
View childView=parent.getChildAt(i);
int index=parent.getChildAdapterPosition(childView);
if (index==)
continue;
float top=childView.getTop()-mDividerHeight;
float left=parent.getPaddingLeft();
float right=parent.getWidth()-parent.getPaddingRight();
float bottom=childView.getTop();
c.drawRect(left,top,right,bottom,mPaint);
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
/**计算出每一个ItemView的偏移量
* @param outRect
* @param view
* @param parent
* @param state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getChildAdapterPosition(view)!=){
outRect.top=;
mDividerHeight=;
}
}
}
这里需要注意的是在onDraw()方法中计算分割线的rect坐标。从列表上看,分割线就相当于从第二个item开始,坐标为getPaddingLeft(),iteView.getTop-mDividerHeight,getWidth()-getPaddingRight(),itemView.getTop()的一个矩形区域