RecyclerView梳理:点击长按事件、分割线、拖曳排序、滑动
删除
这次主要是把 RecyclerView ⽐较常⽤的基本的点,在这⾥集中整理⼀下。从这篇⽂章主要梳理以下⼏点:
优雅的实现:item 点击事件 & item 长点击事件
RecyclerView 添加 divider 的标准姿势
RecyclerView 实现 item 的拖曳排序和滑动删除
拖曳排序时,限制⾸个 item 固定的实现
先看⼀下最终的效果图:
⾃从 RecyclerView 发布以来,由于其⾼度的可交互性被⼴泛使⽤。相信⼤家肯定对它的使⽤⽅法已经⾮常熟练了,今天主要是为⼤家总结⼀下较正常⽤法更加优雅的⽅式。
如果你想再回顾⼀下 RecyclerView 的基本使⽤⽅法,推荐鸿洋的这篇⽂章:
优雅的实现:item 点击事件 & item 长点击事件
使⽤⽅式
RecyclerView 的 api 虽然没有提供 onItemClickListener 但是提供了 addOnItemTouchListener() ⽅法,既然可以添加触摸监听,那么我们完全可以获取触摸⼿势来识别点击事件,然后通过触摸坐标来判断点击的是哪⼀个item。
mRecyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(mRecyclerView) {
@Override
public void onItemClick(RecyclerView.ViewHolder viewHolder) {
//TODO item 点击事件
}
@Override
public void onLongClick(RecyclerView.ViewHolder viewHolder) {
//TODO item 长按事件
}
});
复制代码
其中 OnRecyclerItemClickListener 是⾃定义的⼀个触摸监听器,代码如下:
public abstract class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener{
private GestureDetectorCompat mGestureDetectorCompat;//⼿势探测器
private RecyclerView mRecyclerView;
public OnRecyclerItemClickListener(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mGestureDetectorCompat = new Context(),
new ItemTouchHelperGestureListener());
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
return fal;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public abstract void onItemClick(RecyclerView.ViewHolder viewHolder);
public abstract void onLongClick(RecyclerView.ViewHolder viewHolder);
}
复制代码
GestureDetectorCompat 中传⼊了⼀个 ItemTouchHelperGestureListener,代码如下:
private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener{
//⼀次单独的轻触抬起⼿指操作,就是普通的点击事件
@Override
public boolean onSingleTapUp(MotionEvent e) {
View childViewUnder = mRecyclerView.X(), e.getY());
if (childViewUnder != null) {
RecyclerView.ViewHolder childViewHolder = ChildViewHolder(childViewUnder);
onItemClick(childViewHolder);
}
return true;
}
//长按屏幕超过⼀定时长,就会触发,就是长按事件
@Override
public void onLongPress(MotionEvent e) {
View childViewUnder = mRecyclerView.X(), e.getY());
if (childViewUnder != null) {
卧室的英文RecyclerView.ViewHolder childViewHolder = ChildViewHolder(childViewUnder);
onLongClick(childViewHolder);
}
}
}
复制代码
原理分析
上⾯的代码很简单没什么复杂的地⽅,就是通过⼀个⼿势探测器 GestureDetectorCompat 来探测屏幕事件,然后通过⼿势监听器SimpleOnGestureListener 来识别⼿势事件的种类,然后调⽤我们设置的对应的回调⽅法。这⾥值得说的是:当获取到了 RecyclerView 的点击事件和触摸事件数据 MotionEv
ent,那么如何才能知道点击的是哪⼀个 item 呢?
RecyclerView已经为我们提供了这样的⽅法:findChildViewUnder()。安慰人的话语
我们可以通过这个⽅法获得点击的 item ,同时我们调⽤ RecyclerView 的另⼀个⽅法 getChildViewHolder(),可以获得该 item 的ViewHolder,最后再回调我们定义的虚⽅法 onItemClick() 就ok了,这样我们就可以在外部实现该⽅法来获得 item 的点击事件了。RecyclerView 添加 divider 的标准姿势
当你想给条⽬间添加 divider 时,你可能⾃然⽽然的去尝试这种⽅式:
雪绒花歌词<android.support.v7.widget.RecyclerView
android:divider="#ffff0000"
android:dividerHeight="10dp"
现代艺术家android:layout_width="match_parent"
android:layout_height="match_parent" />
复制代码
其实 RecyclerView 是没有这两个属性的,就算你写上也不会有任何效果。
当然你还可以通过给 item 的最外层布局设置⼀个 margin 值,甚⾄你还可以专门在 item 布局中的适当地⽅添加⼀个⾼度/宽度为 1 的带背景的 View 作为 divider,这两种⽅法呢,确实有效果,但是不够优雅,有时还可能带来⼀些想不到的问题。
其实官⽅还是为我们提供了为 RecyclerView 添加分割线的⽅式的,那就是⽅法: mRecyclerView.addItemDecoration() 。该⽅法的参数为RecyclerView.ItemDecoration,该类为抽象类,且官⽅⽬前并没有提供默认的实现类,我们只能⾃⼰来实现。
使⽤⽅式
列表布局的分割线实例:
public class DividerListItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerListItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
tOrientation(orientation);
}
public DividerListItemDecoration(Context context, int orientation, int drawableId) {
mDivider = Drawable(context, drawableId);
tOrientation(orientation);
}
public void tOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
//画线 > 就是画出你想要的分割线样式
植物组织培养
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} el {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = PaddingLeft();
final int right = Width() - PaddingRight();
final int childCount = ChildCount();
for (int i = 0; i < childCount; i++) {
final View child = ChildAt(i);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.Context()); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = Bottom() + params.bottomMargin;
final int bottom = top + IntrinsicHeight();
mDivider.tBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = PaddingTop();
final int bottom = Height() - PaddingBottom();
final int childCount = ChildCount();
for (int i = 0; i < childCount; i++) {
final View child = ChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = Right() + params.rightMargin;
final int right = left + IntrinsicHeight();
mDivider.tBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
//设置条⽬周边的偏移量
@Override
public void getItemOffts(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.t(0, 0, 0, IntrinsicHeight());
} el {
outRect.t(0, 0, IntrinsicWidth(), 0);
}
}
}
复制代码
⽹格布局分割线实例:
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
private int lineWidth = 1;
public DividerGridItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
final TypedArray a = context.obtainStyledAttributes(ATTRS);
好大学mDivider = a.getDrawable(0);
}
public DividerGridItemDecoration(int color) {
mDivider = new ColorDrawable(color);
}
public DividerGridItemDecoration() {
this(Color.parColor("#cccccc"));
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
drawHorizontal(c, parent);
drawVertical(c, parent);
}
private int getSpanCount(RecyclerView parent) {
// 列数二级手术
int spanCount = -1;
RecyclerView.LayoutManager layoutManager = LayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} el if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = ChildCount();
for (int i = 0; i < childCount; i++) {
final View child = ChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.
getLayoutParams();
final int left = Left() - params.leftMargin;
final int right = Right() + params.rightMargin
+ lineWidth;
final int top = Bottom() + params.bottomMargin;
final int bottom = top + lineWidth;
mDivider.tBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int childCount = ChildCount();
for (int i = 0; i < childCount; i++) {
final View child = ChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) LayoutParams(); final int top = Top() - pMargin;
final int bottom = Bottom() + params.bottomMargin;
final int left = Right() + params.rightMargin;
final int right = left + lineWidth;
mDivider.tBounds(left, top, right, bottom);
mDivider.draw(c);
文强简介}
}
private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) {