前言
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问题就解决了~