RecyclerView的简单用法我们已经再熟悉不过了,这里就不多说了,下面主要说一下RecyclerView比较好用的一些功能,先看下效果图
下面我们将对这些功能进行逐一实现
一、添加分隔线
我们知道ListView添加分隔线是一件很简单的事情,我们添加以下代码就可以:
android:divider="@color/colorAccent"
android:dividerHeight="2dp"
但是我们使用RecyclerView添加这些对我们是一点用都没有的。然而RecyclerView提供了更加高级的用法:
addItemDecoration()
有了这个方法我们就可以定制自己的分隔线,我们只要实现RecyclerView.ItemDecoration
下的两个方法
getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
和
onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state)
我们的RecyclerView的ItemView周围实际上可以用下图表示
绿色的部分就是我们的itemView,橙色的部分就是我们的outRect,我们给outRect设置left,top,right,bottom实际上就是设置我们的itemView与其他控件的偏移量,我们这里想设置分隔线,只需要给我们的outRect设置outRect.top=1px,留出1px的间距供我们去绘制分隔线。
最后在onDraw方法中计算出分隔线矩形的left,top,right和bottom绘制出来就可以了
我们这里同时处理了LinearLayoutManager和GridLayoutManager的分隔线,LinearLayoutManager的分隔线比较简单,GridLayoutManger我们需要考虑到最后一列不需要绘制右侧分隔线,最后一行不需要绘制底部分隔线,我们判断是否是右侧或者左侧代码如下:
/**
* 是否是最后一行
* @param parent
* @param position
* @return
*/
public boolean isLastRow(RecyclerView parent,int position){
int itemCount = parent.getAdapter().getItemCount(); //itemView的总数
GridLayoutManager gridLayoutManager = (GridLayoutManager) parent.getLayoutManager();
int spanCount = gridLayoutManager.getSpanCount(); //gridLayoutManager有多少列
itemCount = itemCount - itemCount % spanCount;
if(position>=itemCount)
return true;
else
return false;
}
/**
* 是否是最后一列
* @param parent
* @param position
* @return
*/
public boolean isLastColunm(RecyclerView parent,int position){
GridLayoutManager gridLayoutManager = (GridLayoutManager) parent.getLayoutManager();
int spanCount = gridLayoutManager.getSpanCount();
if((position+1) % spanCount == 0)
return true;
else
return false;
}
具体的计算和绘制的过程:(其实分隔线就是绘制一个矩形就)
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view); //根据itemView获取在RecyclerView中的位置
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) { //网格布局
if (isLastColunm(parent,position)) {
outRect.bottom = (int) dividerHeight; //是最后一列的话,只绘制底部的分隔线
} else if(isLastRow(parent,position)){
outRect.right = (int) dividerHeight; //是最后一行的话,只绘制右侧的分隔线
}else{
outRect.bottom = (int) dividerHeight;
outRect.right = (int) dividerHeight;
}
} else { //线性布局
if (position != 0) {
outRect.top = (int) dividerHeight;
}
}
}
@Override
public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
super.onDraw(canvas, parent, state);
int childCount = parent.getChildCount();
if (parent.getLayoutManager() instanceof GridLayoutManager) {
for (int i = 0; i < childCount; i++) {
View childView = parent.getChildAt(i);
int left = childView.getLeft();
int top = childView.getTop();
int right = childView.getRight();
int bottom = childView.getBottom();
int childAdapterPosition = parent.getChildAdapterPosition(childView);
if (isLastColunm(parent,childAdapterPosition)) { //最后一列的分隔线
canvas.drawRect(left, bottom, right, bottom+dividerHeight, mPaint);
} else if(isLastRow(parent,childAdapterPosition)){ //最后一行的分隔线
canvas.drawRect(right, top, right+dividerHeight, bottom, mPaint);
}else{
canvas.drawRect(left, bottom, right+dividerHeight, bottom+dividerHeight, mPaint);
canvas.drawRect(right, top, right+dividerHeight, bottom, mPaint);
}
}
} else {
//绘制线性布局的itemView之间的分隔线
for (int i = 0; i < childCount; i++) {
View itemView = parent.getChildAt(i);
int childAdapterPosition = parent.getChildAdapterPosition(itemView);
if (childAdapterPosition == 0) { //如果是第一条,不绘制分割线
continue;
}
int dividerLeft = parent.getPaddingLeft();
int dividerTop = (int) (itemView.getTop() - dividerHeight);
int dividerRight = parent.getWidth() - parent.getPaddingRight();
int dividerBottom = itemView.getTop();
canvas.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
}
}
}
二、添加点击和长按
RecyclerView没有提供类似setOnItemCickListener这样的接口来供我们实现单击itemView的操作。RecyclerView提供了addOnItemTouchListener,我们可以实现RecyclerView.OnItemTouchListener这个接口中的两个方法
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
onTouchEvent(RecyclerView rv, MotionEvent e)
熟悉View事件分发机制的,对这两个方法应该都不陌生,我们可以拦截我们的View事件,然后交由GestureDetectorCompat手势识别类进行处理我们的手势,然后根据单击和长按分别调用我们定义的接口回调方法:
//RecyclerView条目的单击
public abstract void onItemClick(RecyclerView.ViewHolder holder);
//RecyclerView的长按
public abstract void onItemLongPress(RecyclerView.ViewHolder holder);
这样我们就轻松的实现了RecyclerView的单击和长按操作
详细代码如下:
@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) {
}
public class OnItemGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
//findChildViewUnder根据点击的x,y坐标找到点击的位置所属的view
View childViewUnder = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if(childViewUnder!=null){
//找到RecyclerView点击x,y位置的View的ViewHolder
RecyclerView.ViewHolder viewHolder = mRecyclerView.getChildViewHolder(childViewUnder);
onItemClick(viewHolder);
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View childViewUnder = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if(childViewUnder!=null){
RecyclerView.ViewHolder childViewHolder = mRecyclerView.getChildViewHolder(childViewUnder);
onItemLongPress(childViewHolder);
}
}
}
这其中
findChildViewUnder
根据我们按下的坐标位置找到我们RecyclerView中的具体某一个itemView
getChildViewHolder
根据我们的ItemView找到相应的ViewHolder
三、拖拽移动、侧滑删除(我们这里同时实现了线性和网格布局的拖动,线性的侧滑删除)
RecyclerView实现侧滑删除是一件非常容易的事情,ItemTouchHelper就是用来辅助RecyclerView来实现各种删除侧滑操作的。我们的只要继承ItemTouchHelper.Callback并实现其中的个方法:
(1)拖拽移动需要我们实现下面两个方法
a:
getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
我们在长按ItemView的时候会调用这个方法,我们可以在这个方法里面定义我们LinearLayoutManager和GridLayoutManager的移动的方法
ItemTouchHelper.UP
ItemTouchHelper.DOWN
ItemTouchHelper.LEFT
ItemTouchHelper.RIGHT
滑动的方向
ItemTouchHelper.START
ItemTouchHelper.END
然后调用makeMovementFlags设置我们移动和滑动的方向
/**
* 当长按itemView移动的时候调用这个方法
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { //因为GridLayout有上下左右四个方向,所以dragFlags设置为上下左右
int dragFlags = android.support.v7.widget.helper.ItemTouchHelper.UP | android.support.v7.widget.helper.ItemTouchHelper.DOWN
| android.support.v7.widget.helper.ItemTouchHelper.LEFT | android.support.v7.widget.helper.ItemTouchHelper.RIGHT;
int swipeFlags = 0; //设置为零表示不能够侧滑删除
return makeMovementFlags(dragFlags, swipeFlags);
} else {
int dragFlags = android.support.v7.widget.helper.ItemTouchHelper.UP | android.support.v7.widget.helper.ItemTouchHelper.DOWN; //线性布局只要上下两个方向
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
b:
onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
当我们长按移动ItemView的时候会不断的走这个方法,在这里我们可以根据长按移动交换itemView的位置,具体实现如下:
/**
* 长按移动itemView的时候调用这个方法
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition(); //按下的位置
int targetPosition = target.getAdapterPosition(); //移动到的位置
if(recyclerView.getLayoutManager() instanceof GridLayoutManager){ //如果是GridLayoutManager,在第0位不能拖动和被覆盖掉
if (targetPosition == 0) {
return false;
}
}
if (fromPosition < targetPosition) {
for (int i = fromPosition; i < targetPosition; i++) {
Collections.swap(mAdapter.getDataList(), i, i + 1);
}
} else {
for (int i = fromPosition; i > targetPosition; i--) {
Collections.swap(mAdapter.getDataList(), i, i - 1);
}
}
mAdapter.notifyItemMoved(fromPosition, targetPosition);
return true;
}
(2)侧滑删除需要我们实现下面这个方法
onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
具体实现
/**
* 侧滑删除的时候调用这个方法
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int adapterPosition = viewHolder.getAdapterPosition();
mAdapter.notifyItemRemoved(adapterPosition);
mAdapter.getDataList().remove(adapterPosition);
}