RecyclerView的事件监听:
1、 点击事件
2、 滑动和拖拽事件
3、 添加标题和滚动事件
一、点击事件
可以单独为每一个item增加一个点击事件,其方法就是在获取都子View时增加View的点击事件,然后通过一个监听接口回调接口,如下:
class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textview);
textView.setOnClickListener(mViewOnClickListener);
}
}
private View.OnClickListener mViewOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnClickListener != null) {
mOnClickListener.onClick(v);
}
}
}
public void setOnClickListener(OnClickListener listener) {
mOnClickListener = listener;
}
效果图如下:
二、滑动和拖拽事件
滑动和拖拽需要依靠辅助类ItemTouchHelper,我们需要重写ItemTouchHelper构造函数的入参
ItemTouchHelper touchHelper = new ItemTouchHelper(new ItemTouchCallback(adapter));
touchHelper.attachToRecyclerView(mRecyclerView);
ItemTouchCallback定义如下
public class ItemTouchCallback extends ItemTouchHelper.Callback {
private ItemTouchListener mItemTouchListener;
public ItemTouchCallback(ItemTouchListener itemTouchListener) {
mItemTouchListener = itemTouchListener;
}
@Override
public boolean isLongPressDragEnabled() {
return super.isLongPressDragEnabled();
}
@Override
public boolean isItemViewSwipeEnabled() {
return super.isItemViewSwipeEnabled();
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mItemTouchListener.onItemRemove(viewHolder.getAdapterPosition());
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int swipeFlag = 0;
int dragFlag = 0;
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
int orientation = ((LinearLayoutManager) layoutManager).getOrientation();
if (orientation == LinearLayoutManager.HORIZONTAL) {
swipeFlag = ItemTouchHelper.UP;
dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else {
swipeFlag = ItemTouchHelper.LEFT;
dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
}
} else if (layoutManager instanceof GridLayoutManager) {
dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN
| ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN
| ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return makeMovementFlags(dragFlag, swipeFlag);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
mItemTouchListener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
}
依次解释各个方法,isLongPressDragEnabled表示是否支持长按拖拽,父类默认返回true,isItemViewSwipeEnabled表示是否支持滑动,父类默认返回true,onSwiped为滑动的回调,
onMove为拖拽的回调,getMovementFlags则是返回拖拽和滑动的标志位,表示可以上下还是左右拖拽或滑动。
另外增加监听接口实现滑动的删除和拖拽的位置互换
@Override
public void onItemRemove(int position) {
mList.remove(position);
notifyItemRemoved(position);
}
@Override
public void onItemMove(int oldPosition, int newPosition) {
Collections.swap(mList, oldPosition, newPosition);
notifyItemMoved(oldPosition, newPosition);
}
效果图如下:
三、添加标题和滚动事件
首先来看下标题和浮动标题如何实现,有两种方案,如果对于标题不需要添加事件,则可以通过类似于画分割线的方式设置偏移量,画出标题;如果需要设置标题触摸事件的话,则需要在item中增加type为标题的item,该代码中使用前一个方案。
我们在画分割线的代码中加入显示标题的代码
private void drawVertical(Canvas c, RecyclerView parent) {
int left, right, top, buttom;
int childCount = parent.getChildCount();
left = parent.getPaddingLeft();
right = parent.getWidth() - left - parent.getPaddingRight();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int position = layoutParams.getViewAdapterPosition();
// 需要显示标题
if (needShowTitle(position, parent)) {
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(10);
mPaint.setTextSize(TITLE_SIZE);
top = child.getTop() - layoutParams.topMargin;
buttom = top + mDrawable.getIntrinsicHeight();
c.drawText(getTitleName(position, parent), left, top, mPaint);
mDrawable.setBounds(left, top, right, buttom);
mDrawable.draw(c);
}
top = child.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
buttom = top + mDrawable.getIntrinsicHeight();
mDrawable.setBounds(left, top, right, buttom);
mDrawable.draw(c);
}
}
这样,标题其实只是通过分割线的原理显示了出来。然后要考虑标题置顶浮动,则需要等所有子View全部绘画完成,所以我们在onDrawOver中完成该项工作
@Override
public void onDrawOver(Canvas c, RecyclerView parent) {
if (!mShowTitle) {
return;
}
int top = parent.getPaddingTop();
int buttom = top + TITLE_SIZE;
int left = parent.getPaddingLeft();
int right = parent.getMeasuredWidth() + left;
mPaint.setStrokeWidth(10);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.GREEN);
c.drawRect(left, top, right, buttom + mDrawable.getIntrinsicHeight(), mPaint);
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
int position = layoutManager.findFirstVisibleItemPosition();
mPaint.setColor(Color.RED);
mPaint.setTextSize(TITLE_SIZE);
c.drawText(getTitleName(position, parent), left, top + TITLE_SIZE, mPaint);
}
实际上浮动标题是显示在RecyclerView最上面,而向上滑动的过程中,浮动标题将覆盖最上面的item或者标题。我们来看下效果图
接下来为了实现如下的效果,我们需要在左侧增加一个列表,当点击左侧选项后,会跳转到右侧对应选项并置顶。
这里的滚动需要通过scrollBy和scrollToPosition两个函数实现,scrollBy用来实现在可见区域的滚动,而scrollToPosition需要区分条件
A、 当跳转到第一个可见item以上的item时,可以直接使用scrollToPosition完成
B、 当在可见区域内使用scrollToPosition时,无法实现item置顶
C、 当跳转到可见区域最后一个item以下的item时,scrollToPosition只能实现item显示到最后一个可见位置,需配合scrollBy使用。如下
if (position != -1) {
mItemList.stopScroll();
mPosition = position;
int topPosition = ((LinearLayoutManager)mItemList.getLayoutManager()).findFirstVisibleItemPosition();
int buttomPosition = ((LinearLayoutManager)mItemList.getLayoutManager()).findLastVisibleItemPosition();
if (position <= topPosition) {
mItemList.scrollToPosition(position);
} else if (position <= buttomPosition) {
int div = position - topPosition;
if (div >= 0 && div < mItemList.getChildCount()) {
mItemList.scrollBy(0, mItemList.getChildAt(div).getTop() - LinearItemDecoration.TITLE_SIZE);
}
} else {
mItemList.scrollToPosition(position);
mScrolled = true;
}
监听滚动事件:
private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (mScrolled) {
super.onScrolled(recyclerView, dx, dy);
int topPosition = ((LinearLayoutManager)mItemList.getLayoutManager()).findFirstVisibleItemPosition();
int div = mPosition - topPosition;
if (div > 0 && div < mItemList.getChildCount()) {
mItemList.scrollBy(0, mItemList.getChildAt(div).getTop() - LinearItemDecoration.TITLE_SIZE);
mScrolled = false;
}
}
}
};
}
效果图如下:
这里发现有个问题,当想实现平滑的效果使用smoothScrollBy和smoothScrollToPosition时,会出现bug,当要滑动到上面不可见item后,要想再滑动到可见的一个item时,无法跳转到对应item,原因是进入position <= buttomPosition后,getChildCount并不是返回当前可见区域的item个数(比实际要多),有遇到过得同学可以解答下原因。