前言
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問題就解決了~