RecyclerView的常見用法總結

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);
    }

源代碼

參考鏈接:

http://www.10tiao.com/html/227/201705/2650239745/1.html

https://mp.weixin.qq.com/s/LsxYOGnp6Yq_9rFHUDAL9A

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章