RecyclerView的简单用法我们已经再熟悉不过了,这里就不多说了,下面主要说一下RecyclerView比较好用的一些功能,先看下效果图

判断recycleview的item是否可见_分隔线

  

判断recycleview的item是否可见_ide_02

判断recycleview的item是否可见_ide_03

下面我们将对这些功能进行逐一实现

一、添加分隔线

我们知道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周围实际上可以用下图表示

判断recycleview的item是否可见_android_04

绿色的部分就是我们的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);
    }