This commit is contained in:
ScorpioMiku
2018-09-02 14:47:43 +08:00
parent d73ad163d5
commit 06e16847b0
8 changed files with 403 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
package com.example.ninefourone.nutritionmaster.adapter;
import android.support.v7.widget.RecyclerView;
/**
* Created by ScorpioMiku on 2018/9/2.
*/
public class CardAdapter extends RecyclerView.Adapter {
}

View File

@@ -0,0 +1,10 @@
package com.example.ninefourone.nutritionmaster.adapter;
import android.support.v7.widget.RecyclerView;
/**
* Created by ScorpioMiku on 2018/9/2.
*/
public class CardHolder extends RecyclerView.ViewHolder {
}

View File

@@ -0,0 +1,25 @@
package com.example.ninefourone.nutritionmaster.layoutmanager;
import android.content.Context;
import android.util.TypedValue;
/**
* 初始化一些配置信息、固定数据
*/
public class CardConfig {
//屏幕上最多同时显示几个Item
public static int MAX_SHOW_COUNT;
//每一级Scale相差0.05ftranslationY相差15dp,translationZ相差0.5dp左右
public static float SCALE_GAP;
public static int TRANS_Y_GAP;
public static int TRANS_Z_GAP;
public static void initConfig(Context context) {
MAX_SHOW_COUNT = 4;
SCALE_GAP = 0.05f;
//这里是把dp转换成px
TRANS_Y_GAP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15f, context.getResources().getDisplayMetrics());
TRANS_Z_GAP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f, context.getResources().getDisplayMetrics());
}
}

View File

@@ -0,0 +1,254 @@
package com.fu.tantancard;
import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.View;
import java.util.List;
/**
* Created by Fu.
* QQ:908323236
* 2017/11/10 11:27
*/
public class CardItemTouchCallBack extends ItemTouchHelper.Callback {
private static final String TAG = "CardItemTouchCallBack";
private RecyclerView mRecyclerView;
private MainActivity.CardAdapter mAdapter;
private List mDatas;
public CardItemTouchCallBack(RecyclerView recyclerView, MainActivity.CardAdapter adapter, List datas) {
this.mRecyclerView = recyclerView;
this.mAdapter = adapter;
this.mDatas = datas;
}
/**
* 是否开启长按拖拽
* true开启
* false,不开启长按退拽
*
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return false;
}
/**
* 是否开启滑动
* true开启
* false,不开启长按退拽
*
* @return
*/
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
/**
* ItemTouchHelper支持设置事件方向并且必须重写当前getMovementFlags来指定支持的方向
* dragFlags 表示拖拽的方向有六个类型的值LEFT、RIGHT、START、END、UP、DOWN
* swipeFlags 表示滑动的方向有六个类型的值LEFT、RIGHT、START、END、UP、DOWN
* 最后要通过makeMovementFlagsdragFlagswipe创建方向的Flag
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
/**
* 由于我们不需要长按拖拽所以直接传入0即可传入0代表不监听
*/
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return makeMovementFlags(0, swipeFlags);
}
/**
* 长按item就可以拖动然后拖动到其他item的时候触发onMove
* 这里我们不需要
*
* @param recyclerView
* @param viewHolder 拖动的viewholder
* @param target 目标位置的viewholder
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
/**
* 把item滑走(飞出屏幕)的时候调用
*
* @param viewHolder 滑动的viewholder
* @param direction 滑动的方向
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//这里来判断画出的方向,左边是4,右边是8,然后可以做一些数据操作
Log.d(TAG, "onSwiped: " + direction);
switch (direction) {
case 4:
Log.d(TAG, "onSwiped: 左边滑出");
mAdapter.addDelCount();
break;
case 8:
Log.d(TAG, "onSwiped: 右边滑出");
mAdapter.addLoveCount();
break;
}
//移除这条数据
Object remove = mDatas.remove(viewHolder.getLayoutPosition());
/** 这个位置可以用来加载数据,当滑到还剩4个或者多少个时可以在后面加载数据添加到mDatas中*/
//这里就为了方便,直接循环了,把移除的元素再添加到末尾
mDatas.add(mDatas.size(), remove);
//刷新
mAdapter.notifyDataSetChanged();
//复位
viewHolder.itemView.setRotation(0);
if (viewHolder instanceof MainActivity.CardAdapter.CardViewHolder) {
MainActivity.CardAdapter.CardViewHolder holder = (MainActivity.CardAdapter.CardViewHolder) viewHolder;
holder.iv_love.setAlpha(0f);
holder.iv_del.setAlpha(0f);
}
}
/**
* 只要拖动、滑动了item,就会触发这个方法,而且是动的过程中会一直触发
* 所以动画效果就是在这个方法中来实现的
*
* @param c
* @param recyclerView
* @param viewHolder
* @param dX
* @param dY
* @param actionState
* @param isCurrentlyActive
*/
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
double swipeValue = Math.sqrt(dX * dX + dY * dY); //滑动离中心的距离
double fraction = swipeValue / (mRecyclerView.getWidth() * 0.5f);
//边界修正 最大为1
if (fraction > 1) {
fraction = 1;
}
/**
* 调整每个子view的缩放、位移之类的
*/
int childCount = recyclerView.getChildCount(); //拿到子view的数量
isUpOrDown(mRecyclerView.getChildAt(childCount - 1));
for (int i = 0; i < childCount; i++) {
/** 拿到子view 注意这里,先绘制的i=0所以最下面一层view的i=0,最上面的i=3*/
View childView = recyclerView.getChildAt(i);
int level = childCount - i - 1; //转换一下,level代表层数,最上面是第0层
if (level > 0) {
//下面层,每一层的水平方向都要增大
childView.setScaleX((float) (1 - CardConfig.SCALE_GAP * level + fraction * CardConfig.SCALE_GAP));
if (level < CardConfig.MAX_SHOW_COUNT - 1) {
//1 2层
childView.setScaleY((float) (1 - CardConfig.SCALE_GAP * level + fraction * CardConfig.SCALE_GAP));
childView.setTranslationY((float) (CardConfig.TRANS_Y_GAP * level - fraction * CardConfig.TRANS_Y_GAP));
childView.setTranslationZ((float) (CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1 - level)
+ fraction * CardConfig.TRANS_Z_GAP));
} else {
//最下面一层,3层,这层不用变,所以这里不用写
}
} else {
//第0层
//拿到水平方向的偏移比率
float xFraction = dX / (mRecyclerView.getWidth() * 0.5f);
//边界修正,有正有负,因为旋转有两个方向
if (xFraction > 1) {
xFraction = 1;
} else if (xFraction < -1) {
xFraction = -1;
}
//第一层左右滑动的时候稍微有点旋转
childView.setRotation(xFraction * 15); //这里最多旋转15度
if (viewHolder instanceof MainActivity.CardAdapter.CardViewHolder) {
MainActivity.CardAdapter.CardViewHolder holder = (MainActivity.CardAdapter.CardViewHolder) viewHolder;
if (dX > 0) {
//右滑,显示爱心
holder.iv_love.setAlpha(xFraction);
} else if (dX < 0) {
//左滑,显示叉,注意这里xFraction为负数所以要取反
holder.iv_del.setAlpha(-xFraction);
} else {
holder.iv_love.setAlpha(0f);
holder.iv_del.setAlpha(0f);
}
}
}
}
}
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
Log.i(TAG, "getSwipeThreshold: ");
if (isUpOrDown(viewHolder.itemView)) { //如果是向上或者向下滑动
return Float.MAX_VALUE; //就返回阈值为很大
}
return super.getSwipeThreshold(viewHolder);
}
/**
* 获得逃脱(swipe)速度
*
* @param defaultValue
* @return
*/
@Override
public float getSwipeEscapeVelocity(float defaultValue) {
Log.d(TAG, "getSwipeEscapeVelocity: " + defaultValue);
View topView = mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1);
if (isUpOrDown(topView)) { //如果是向上或者向下滑动
return Float.MAX_VALUE; //就返回阈值为很大
}
return super.getSwipeEscapeVelocity(defaultValue);
}
/**
* 获得swipe的速度阈值
*
* @param defaultValue
* @return
*/
@Override
public float getSwipeVelocityThreshold(float defaultValue) {
Log.d(TAG, "getSwipeVelocityThreshold: " + defaultValue);
View topView = mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1);
if (isUpOrDown(topView)) { //如果是向上或者向下滑动
return Float.MAX_VALUE; //就返回阈值为很大
}
return super.getSwipeVelocityThreshold(defaultValue);
}
/**
* 判断是否是向上滑或者向下滑
*/
private boolean isUpOrDown(View topView) {
float x = topView.getX();
float y = topView.getY();
int left = topView.getLeft();
int top = topView.getTop();
if (Math.pow(x - left, 2) > Math.pow(y - top, 2)) {
//水平方向大于垂直方向
// Log.i(TAG, "isUpOrDown: 不是");
return false;
} else {
return true;
// Log.i(TAG, "isUpOrDown: 是");
}
}
}

View File

@@ -0,0 +1,98 @@
package com.example.ninefourone.nutritionmaster.layoutmanager;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by ScorpioMiku on 2018/9/2.
*/
public class SwipeCardLayoutManager extends RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
/**
* 在这里面给子view布局,也就是item
*
* @param recycler
* @param state
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
//在布局之前将所有的子View先Detach掉放入到Scrap缓存中
detachAndScrapAttachedViews(recycler);
//拿到总item数量
int itemCount = getItemCount();
if (itemCount < 1) {//没有item当然就没必要布局了
return;
}
int bottomPosition; //用来记录最底层view的postion
if (itemCount < CardConfig.MAX_SHOW_COUNT) { //如果不足最大数量(4个)
//那么最底层就是最后一条数据对应的position
bottomPosition = itemCount - 1;
} else {
//否则最底层就是第MAX_SHOW_COUNT(4)条数据对应的position
bottomPosition = CardConfig.MAX_SHOW_COUNT - 1;
}
/**
* 这里开始布局且绘制子view
* 注意:这里要先从最底层开始绘制,因为后绘制的才能覆盖先绘制的,
* 滑动的时候是滑最上面一层的,也就是后绘制的
* position也是层数
*/
for (int position = bottomPosition; position >= 0; position--) {
//根据position找recycler要itemview
View view = recycler.getViewForPosition(position);
//将子View添加至RecyclerView中
addView(view);
//测量子view并且把Margin也作为子控件的一部分
measureChildWithMargins(view, 0, 0);
//宽度空隙 getWidth()得到Recycler控件的宽度,getDecoratedMeasuredWidth(view)拿到子view的宽度
int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
//高度空隙
int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
//给子view布局,这里居中了
layoutDecoratedWithMargins(view, widthSpace / 2, 200,
widthSpace / 2 + getDecoratedMeasuredWidth(view),
getDecoratedMeasuredHeight(view) + 200);
/**
* 下面要调整每一层itemview的的大小及Y轴和Z轴的偏移
* 最上面一层第0层的Scale为1,translationY为0
* 依次往下,每层比上面一层:
* (1)Scale相差0.05f
* (2)translationY相差7dp
* (3)translationZ相差1dp
*
* 注意:最后一层,除了水平方向的大小其他都与上一层一样,所以要特殊判断
*/
if (position > 0) { //大于0就是不是最上面那层
//依次往下,每层都要水平方向缩小
view.setScaleX(1 - CardConfig.SCALE_GAP * position);
if (position < CardConfig.MAX_SHOW_COUNT - 1) {
//如果,不是最后一层,就都要调整
view.setScaleY(1 - CardConfig.SCALE_GAP * position); //垂直方向缩小
view.setTranslationY(CardConfig.TRANS_Y_GAP * position); //向下平移
view.setTranslationZ(CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1 - position)); //Z轴方向的平移
} else {
//否则,就是最后一层,与上一层保持一致
view.setScaleY(1 - CardConfig.SCALE_GAP * (position - 1)); //垂直方向缩小
view.setTranslationY(CardConfig.TRANS_Y_GAP * (position - 1)); //向下平移
view.setTranslationZ(CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1 - (position - 1))); //Z轴方向的平移
}
} else {
//否则是第0层(最上面那层),只需调整Z轴高度
view.setTranslationZ(CardConfig.TRANS_Z_GAP * (CardConfig.MAX_SHOW_COUNT - 1)); //Z轴方向的平移
}
}
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.constraint.ConstraintLayout>