藝術控件RecyclerView的交互動畫&bug解決

前言

        RecyclerView是Google在support-v7裏面添加的控件,是5.0 Material Design模式下的一員,在衆多的App中使用非常頻繁,之前是ListView現在是RecyclerView,想比之下RecyclerView更加的靈活,高內聚低耦合,將ListView功能進行了拆分,各個類各司其職構成了現在的RecyclerView。現在我們來玩一下RecyclerView的交互動畫和解決一下bug。

效果~

Part 1、RecyclerView動畫交互

RecyclerView的設置

        rv = (RecyclerView) findViewById(R.id.rv);
        rv.setLayoutManager(new LinearLayoutManager(this));
        adapter = new MyAdapter(DataUtils.init(),this);
        rv.addItemDecoration(new DividerItemDecoration(this,LinearLayoutManager.VERTICAL));
        rv.setAdapter(adapter);
        helper = new ItemTouchHelper(new MyItemTouchCallBack(adapter));
        helper.attachToRecyclerView(rv);
tips:
1、DividerItemDecoration() : 是Android support-v7提供的默認分隔線

2、要實現拖拽、側滑效果需要繼承ItemTouchCallBack類

3、attachToRecyclerView() : 將ItemTouCallBack包裝之後的ItemTouchHelper類關聯到RecyclerView上

接下來我們定義MyItemTouchCallBack類繼承ItemTouchCallBack類

public class MyItemTouchCallBack extends ItemTouchHelper.Callback {
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        int flag = makeMovementFlags(dragFlag, swipeFlag);
        return flag;
    }
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder src, RecyclerView.ViewHolder target) {
        return true;
    }
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    }
}
tips:

1、getMoveMentFlags() : CallBack回調監聽時先調用,用來判斷當前是什麼動作(上下還是左右),上面指定的是上下拖拽、左右側滑的效果,如果你的RecyclerView是橫向則相反

2、onMove() : 當移動的時候回調的拖拽方法

3、onSwiped()  : 當側滑的時候回調

效果~


然而當拖拽、側滑的時候其它Item並沒有隨着移動,所以要實現這種效果需要讓Adapter不斷的去刷新notifyItemMoved & notifyItemRemoved;又因爲在拖拽和側滑的過程中會不斷調用onMove和onSwipe方法,所以只需要在這兩個方法裏面調用Adapter的notify方法即可。

定義接口,通過接口對象調用

public interface ItemTouchMoveListener {

    /**
     * 當拖拽的時候回調</br>
     * 可以在此方法裏面實現:拖拽條目並實現刷新效果
     * @param fromPosition 從什麼位置拖
     * @param toPosition	到什麼位置
     * @return 是否執行了move
     */
    boolean onItemMove(int fromPosition, int toPosition);

    /**
     * 當條目被移除是回調
     * @param position 移除的位置
     * @return
     */
    boolean onItemRemove(int position);
}
調用的方法

    //當移動的時候回調的方法--拖拽
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder src, RecyclerView.ViewHolder target) {
        if (src.getItemViewType() != target.getItemViewType()) {
            return false;
        }
        // 在拖拽的過程當中不斷地調用adapter.notifyItemMoved(from,to);
        mTouchMoveListener.onItemMove(src.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    //側滑的時候回調的
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        mTouchMoveListener.onItemRemove(viewHolder.getAdapterPosition());
    }
tips:

1、

        if (src.getItemViewType() != target.getItemViewType()) {
            return false;
        }
判斷的意義在於在RecyclerView可能每個Item的類型是不同的,但是默認情況是相同的可以忽略

被調用的方法

    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        Collections.swap(list, fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    @Override
    public boolean onItemRemove(int position) {
        list.remove(position);
        notifyItemRemoved(position);
        return true;
    }
tips:

1、不管是移動還是移除都遵循兩步:(1)處理數據 (2)進行刷新

這樣Item的動畫就形成了

效果~

這裏也提一下,雖然RecyclerView提供了拖拽和側滑動畫,但是你也可以手動的去觸發它們

helper.startDrag(viewHolder);

從效果圖中看到選中和未選中背景是一樣樣的,看着挺不爽,幸運的是在ItemTouchCallBack類提供了

    //爲選中設置背景顏色
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        //判斷選中狀態
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundColor(Color.parseColor("#66666666"));
        }
        super.onSelectedChanged(viewHolder, actionState);
    }
    //取消選中的背景顏色
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        if (viewHolder != null) {
            viewHolder.itemView.setBackgroundColor(Color.WHITE);
        }
        super.clearView(recyclerView, viewHolder);
    }
就這樣就解決了我們的需求,不得不說RecyclerView很貼心

最後RecyclerView當然也提供給我們實現每個Item滑動時候的動畫效果,只需要在onChildDraw()實現相應的方法即可

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        //dX:水平方向移動的增量  0~1
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            float alpha = 1 - Math.abs(dX) / viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);//1~0
            viewHolder.itemView.setScaleX(alpha);//1~0
            viewHolder.itemView.setScaleY(alpha);//1~0
        }
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
效果~

ok,效果完成

Part 2、RecyclerView交互動畫bug解決

當最後的那個效果如果你向下滑動會看到會有好多空的Item

爲什麼會發生這種情況呢?其實你仔細觀察空白的是有規律的,像RecyclerView和ListView都會去複用ItemView,當你移除之後便沒有了複用的View,所以會顯示空白。

解決辦法很多,只需要你在恢復刪除的ItemView即可

因爲每當刪除ItemView時會調用clearView方法,所以在這裏恢復比較合適

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        if (viewHolder != null) {
            viewHolder.itemView.setBackgroundColor(Color.WHITE);
            // 恢復
            viewHolder.itemView.setAlpha(1);//1~0
            viewHolder.itemView.setScaleX(1);//1~0
            viewHolder.itemView.setScaleY(1);//1~0
        }
        super.clearView(recyclerView, viewHolder);
    }
這樣,bug問題就解決了~






發佈了59 篇原創文章 · 獲贊 10 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章