11 | *
12 | * 作者:zhangxutong 13 | * 邮箱:mcxtzhang@163.com 14 | * 主页:http://blog.csdn.net/zxt0601 15 | * 时间: 16/12/18. 16 | */ 17 | 18 | public class CardConfig { 19 | //屏幕上最多同时显示几个Item 20 | public static int MAX_SHOW_COUNT; 21 | 22 | //每一级Scale相差0.05f,translationY相差7dp左右 23 | public static float SCALE_GAP; 24 | public static int TRANS_Y_GAP; 25 | 26 | public static void initConfig(Context context) { 27 | MAX_SHOW_COUNT = 4; 28 | SCALE_GAP = 0.05f; 29 | TRANS_Y_GAP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /layoutmanager/src/main/java/com/mcxtzhang/layoutmanager/swipecard/OverLayCardLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.mcxtzhang.layoutmanager.swipecard; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.util.Log; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | /** 9 | * 介绍:参考人人影视 最多排列四个 10 | * 重叠卡片布局 11 | * 作者:zhangxutong 12 | * 邮箱:mcxtzhang@163.com 13 | * 主页:http://blog.csdn.net/zxt0601 14 | * 时间: 16/12/15. 15 | */ 16 | 17 | public class OverLayCardLayoutManager extends RecyclerView.LayoutManager { 18 | private static final String TAG = "swipecard"; 19 | 20 | @Override 21 | public RecyclerView.LayoutParams generateDefaultLayoutParams() { 22 | return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 23 | } 24 | 25 | @Override 26 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 27 | Log.e(TAG, "onLayoutChildren() called with: recycler = [" + recycler + "], state = [" + state + "]"); 28 | detachAndScrapAttachedViews(recycler); 29 | int itemCount = getItemCount(); 30 | if (itemCount < 1) { 31 | return; 32 | } 33 | //top-3View的position 34 | int bottomPosition; 35 | //边界处理 36 | if (itemCount < CardConfig.MAX_SHOW_COUNT) { 37 | bottomPosition = 0; 38 | } else { 39 | bottomPosition = itemCount - CardConfig.MAX_SHOW_COUNT; 40 | } 41 | 42 | //从可见的最底层View开始layout,依次层叠上去 43 | for (int position = bottomPosition; position < itemCount; position++) { 44 | View view = recycler.getViewForPosition(position); 45 | addView(view); 46 | measureChildWithMargins(view, 0, 0); 47 | int widthSpace = getWidth() - getDecoratedMeasuredWidth(view); 48 | int heightSpace = getHeight() - getDecoratedMeasuredHeight(view); 49 | //我们在布局时,将childView居中处理,这里也可以改为只水平居中 50 | layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2, 51 | widthSpace / 2 + getDecoratedMeasuredWidth(view), 52 | heightSpace / 2 + getDecoratedMeasuredHeight(view)); 53 | /** 54 | * TopView的Scale 为1,translationY 0 55 | * 每一级Scale相差0.05f,translationY相差7dp左右 56 | * 57 | * 观察人人影视的UI,拖动时,topView被拖动,Scale不变,一直为1. 58 | * top-1View 的Scale慢慢变化至1,translation也慢慢恢复0 59 | * top-2View的Scale慢慢变化至 top-1View的Scale,translation 也慢慢变化只top-1View的translation 60 | * top-3View的Scale要变化,translation岿然不动 61 | */ 62 | 63 | //第几层,举例子,count =7, 最后一个TopView(6)是第0层, 64 | int level = itemCount - position - 1; 65 | //除了顶层不需要缩小和位移 66 | if (level > 0 /*&& level < mShowCount - 1*/) { 67 | //每一层都需要X方向的缩小 68 | view.setScaleX(1 - CardConfig.SCALE_GAP * level); 69 | //前N层,依次向下位移和Y方向的缩小 70 | if (level < CardConfig.MAX_SHOW_COUNT - 1) { 71 | view.setTranslationY(CardConfig.TRANS_Y_GAP * level); 72 | view.setScaleY(1 - CardConfig.SCALE_GAP * level); 73 | } else {//第N层在 向下位移和Y方向的缩小的成都与 N-1层保持一致 74 | view.setTranslationY(CardConfig.TRANS_Y_GAP * (level - 1)); 75 | view.setScaleY(1 - CardConfig.SCALE_GAP * (level - 1)); 76 | } 77 | } 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /layoutmanager/src/main/java/com/mcxtzhang/layoutmanager/swipecard/RenRenCallback.java: -------------------------------------------------------------------------------- 1 | package com.mcxtzhang.layoutmanager.swipecard; 2 | 3 | import android.graphics.Canvas; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.support.v7.widget.helper.ItemTouchHelper; 6 | import android.view.View; 7 | 8 | import java.util.List; 9 | 10 | import static com.mcxtzhang.layoutmanager.swipecard.CardConfig.MAX_SHOW_COUNT; 11 | import static com.mcxtzhang.layoutmanager.swipecard.CardConfig.SCALE_GAP; 12 | import static com.mcxtzhang.layoutmanager.swipecard.CardConfig.TRANS_Y_GAP; 13 | 14 | /** 15 | * 介绍:人人影视效果的Callback 16 | * 作者:zhangxutong 17 | * 邮箱:mcxtzhang@163.com 18 | * 主页:http://blog.csdn.net/zxt0601 19 | * 时间: 16/12/18. 20 | */ 21 | 22 | public class RenRenCallback extends ItemTouchHelper.SimpleCallback { 23 | 24 | protected RecyclerView mRv; 25 | protected List mDatas; 26 | protected RecyclerView.Adapter mAdapter; 27 | 28 | public RenRenCallback(RecyclerView rv, RecyclerView.Adapter adapter, List datas) { 29 | this(0, 30 | ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT, 31 | rv, adapter, datas); 32 | } 33 | 34 | public RenRenCallback(int dragDirs, int swipeDirs 35 | , RecyclerView rv, RecyclerView.Adapter adapter, List datas) { 36 | super(dragDirs, swipeDirs); 37 | mRv = rv; 38 | mAdapter = adapter; 39 | mDatas = datas; 40 | } 41 | 42 | //水平方向是否可以被回收掉的阈值 43 | public float getThreshold(RecyclerView.ViewHolder viewHolder) { 44 | //2016 12 26 考虑 探探垂直上下方向滑动,不删除卡片,这里参照源码写死0.5f 45 | return mRv.getWidth() * /*getSwipeThreshold(viewHolder)*/ 0.5f; 46 | } 47 | 48 | @Override 49 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 50 | return false; 51 | } 52 | 53 | @Override 54 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { 55 | //Log.e("swipecard", "onSwiped() called with: viewHolder = [" + viewHolder + "], direction = [" + direction + "]"); 56 | //rollBack(viewHolder); 57 | //★实现循环的要点 58 | Object remove = mDatas.remove(viewHolder.getLayoutPosition()); 59 | mDatas.add(0, remove); 60 | mAdapter.notifyDataSetChanged(); 61 | 62 | 63 | } 64 | 65 | @Override 66 | public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { 67 | super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); 68 | //Log.e("swipecard", "onChildDraw() viewHolder = [" + viewHolder + "], dX = [" + dX + "], dY = [" + dY + "], actionState = [" + actionState + "], isCurrentlyActive = [" + isCurrentlyActive + "]"); 69 | //人人影视的效果 70 | //if (isCurrentlyActive) { 71 | //先根据滑动的dxdy 算出现在动画的比例系数fraction 72 | double swipValue = Math.sqrt(dX * dX + dY * dY); 73 | double fraction = swipValue / getThreshold(viewHolder); 74 | //边界修正 最大为1 75 | if (fraction > 1) { 76 | fraction = 1; 77 | } 78 | //对每个ChildView进行缩放 位移 79 | int childCount = recyclerView.getChildCount(); 80 | for (int i = 0; i < childCount; i++) { 81 | View child = recyclerView.getChildAt(i); 82 | //第几层,举例子,count =7, 最后一个TopView(6)是第0层, 83 | int level = childCount - i - 1; 84 | if (level > 0) { 85 | child.setScaleX((float) (1 - SCALE_GAP * level + fraction * SCALE_GAP)); 86 | 87 | if (level < MAX_SHOW_COUNT - 1) { 88 | child.setScaleY((float) (1 - SCALE_GAP * level + fraction * SCALE_GAP)); 89 | child.setTranslationY((float) (TRANS_Y_GAP * level - fraction * TRANS_Y_GAP)); 90 | } else { 91 | //child.setTranslationY((float) (mTranslationYGap * (level - 1) - fraction * mTranslationYGap)); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':layoutmanager' 2 | --------------------------------------------------------------------------------