cardview
This commit is contained in:
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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.05f,translationY相差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());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
* 最后要通过makeMovementFlags(dragFlag,swipe)创建方向的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: 是");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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轴方向的平移
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
app/src/main/res/layout/card_item.xml
Normal file
6
app/src/main/res/layout/card_item.xml
Normal 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>
|
||||
Reference in New Issue
Block a user