RecyclerView使用方法(二)

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个数(比实际要多),有遇到过得同学可以解答下原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值