一 效果图
二 需求
- 一组包含图片的组件,依次显示,陌陌的滑动删除效果
- 最上面只显示一张图片和内容,下面预加载几张,能够看到预加载的边框部分,其他被最上面遮盖
- 向上、下、左、右触摸滑动一段距离,当前卡片的内容会被移除
- 手指滑动距离不足时,恢复原先效果
- 随着滑动,后面要加载的项会缩放,有一个“向上浮现”的效果
三 需求分析
- RecyclerView展示列表数据
- 自定义LayoutManager重写onLayoutChildren实现每一项数据的显示摆放
- 实现ItemTouchHelper.SimpleCallback用于和用户进行交互,对拖拽进行处理
- SimpleCallback的onChildDraw方法控制每个child的绘制,在其绘制之前对每个child进行动画效果从而实现缩放、平移
四 关键代码
SwipeCardLayoutManager
public class SwipeCardLayoutManager extends RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
// super.onLayoutChildren(recycler, state);
//h缓存的核心机制之一
detachAndScrapAttachedViews(recycler);
int itemCount = getItemCount();
int initPosition;
if (itemCount < CardConfig.MAX_SHOW_COUNT) {
initPosition = 0;
} else {
initPosition = itemCount - CardConfig.MAX_SHOW_COUNT;
}
for (int position = initPosition; position < itemCount; position++) {
//从缓存获取view
View view = recycler.getViewForPosition(position);
addView(view);
measureChild(view, 0, 0);
int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
layoutDecorated(view,
widthSpace/2,
heightSpace/2,
widthSpace/2 + getDecoratedMeasuredWidth(view),
heightSpace/2 + getDecoratedMeasuredHeight(view));
// 均匀的缩放和位移
int level = itemCount - position - 1;
if (level > 0) {
view.setScaleX(1 - CardConfig.SCALE_GAP * level);
view.setScaleY(1- CardConfig.SCALE_GAP * level);
if (level < CardConfig.MAX_SHOW_COUNT -1) {
view.setTranslationY(CardConfig.TRANS_Y_GAP* level);
} else {
view.setTranslationY(CardConfig.TRANS_Y_GAP* (level - 1));
}
}
}
}
}
SwipeCardCallback
public class SwipeCardCallback extends ItemTouchHelper.SimpleCallback {
private RecyclerView mRv;
private RecyclerView.Adapter mAdapter;
private List mDatas;
public SwipeCardCallback(int dragDirs, int swipeDirs, RecyclerView.Adapter adapter, List datas,
RecyclerView recyclerView) {
super(0, ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT);
mRv = recyclerView;
mAdapter = adapter;
mDatas = datas;
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
Object object = mDatas.remove(viewHolder.getLayoutPosition());
mDatas.add(0, object);
mAdapter.notifyItemMoved(viewHolder.getLayoutPosition(), 0);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
double maxDistance = recyclerView.getWidth() * 0.5f;
double distance = Math.sqrt(dX * dX + dY * dY);
double fraction = distance / maxDistance;
if (fraction > 1) {
fraction = 1;
}
int itemCount = recyclerView.getChildCount();
for (int i = 0; i < itemCount; i++) {
View view = recyclerView.getChildAt(i);
int level = itemCount - i - 1;
if (level >= 0) {
if (level < CardConfig.MAX_SHOW_COUNT -1) {
view.setTranslationY((float) (CardConfig.TRANS_Y_GAP *level - fraction * CardConfig.TRANS_Y_GAP));
view.setScaleX((float) (1 - CardConfig.SCALE_GAP * level + fraction *CardConfig.SCALE_GAP));
view.setScaleY((float) (1 - CardConfig.SCALE_GAP * level + fraction *CardConfig.SCALE_GAP));
// view.setAlpha((float) (1 - 0.1 * level + fraction *CardConfig.SCALE_GAP));
} else if (level == CardConfig.MAX_SHOW_COUNT -1){ // 控制的是最下层的Item
view.setTranslationY((float) (CardConfig.TRANS_Y_GAP *(level -1)));
view.setScaleX((float) (1 - CardConfig.SCALE_GAP * (level- 1)));
view.setScaleY((float) (1 - CardConfig.SCALE_GAP * (level -1)));
}
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
return 100;
}
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return 0.5f;
}
}