共6个文件MainActivityRoundImageViewCardConfigCardItemTouchHelperCallbackCardLayoutManagerOnSwipeListener
package com.yuqirong.cardswipeview; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Toast; import java.util.ArrayList; import java.util.List; import com.yuqirong.cardswipeview.cardview.CardConfig; import com.yuqirong.cardswipeview.cardview.CardItemTouchHelperCallback; import com.yuqirong.cardswipeview.cardview.CardLayoutManager; import com.yuqirong.cardswipeview.cardview.OnSwipeListener; public class MainActivity extends AppCompatActivity { private List<Integer> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); } private void initView() { final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(new MyAdapter()); CardItemTouchHelperCallback cardCallback = new CardItemTouchHelperCallback(recyclerView.getAdapter(), list); cardCallback.setOnSwipedListener(new OnSwipeListener<Integer>() { @Override public void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction) { MyAdapter.MyViewHolder myHolder = (MyAdapter.MyViewHolder) viewHolder; viewHolder.itemView.setAlpha(1 - Math.abs(ratio) * 0.2f); if (direction == CardConfig.SWIPING_LEFT) { myHolder.dislikeImageView.setAlpha(Math.abs(ratio)); } else if (direction == CardConfig.SWIPING_RIGHT) { myHolder.likeImageView.setAlpha(Math.abs(ratio)); } else { myHolder.dislikeImageView.setAlpha(0f); myHolder.likeImageView.setAlpha(0f); } } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, Integer o, int direction) { MyAdapter.MyViewHolder myHolder = (MyAdapter.MyViewHolder) viewHolder; viewHolder.itemView.setAlpha(1f); myHolder.dislikeImageView.setAlpha(0f); myHolder.likeImageView.setAlpha(0f); Toast.makeText(MainActivity.this, direction == CardConfig.SWIPED_LEFT ? "swiped left" : "swiped right", Toast.LENGTH_SHORT).show(); } @Override public void onSwipedClear() { Toast.makeText(MainActivity.this, "data clear", Toast.LENGTH_SHORT).show(); recyclerView.postDelayed(new Runnable() { @Override public void run() { initData(); recyclerView.getAdapter().notifyDataSetChanged(); } }, 3000L); } }); final ItemTouchHelper touchHelper = new ItemTouchHelper(cardCallback); final CardLayoutManager cardLayoutManager = new CardLayoutManager(recyclerView, touchHelper); recyclerView.setLayoutManager(cardLayoutManager); touchHelper.attachToRecyclerView(recyclerView); } private void initData() { list.add(R.drawable.img_avatar_01); list.add(R.drawable.img_avatar_02); list.add(R.drawable.img_avatar_03); list.add(R.drawable.img_avatar_04); list.add(R.drawable.img_avatar_05); list.add(R.drawable.img_avatar_06); list.add(R.drawable.img_avatar_07); } private class MyAdapter extends RecyclerView.Adapter { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ImageView avatarImageView = ((MyViewHolder) holder).avatarImageView; avatarImageView.setImageResource(list.get(position)); } @Override public int getItemCount() { return list.size(); } class MyViewHolder extends RecyclerView.ViewHolder { ImageView avatarImageView; ImageView likeImageView; ImageView dislikeImageView; MyViewHolder(View itemView) { super(itemView); avatarImageView = (ImageView) itemView.findViewById(R.id.iv_avatar); likeImageView = (ImageView) itemView.findViewById(R.id.iv_like); dislikeImageView = (ImageView) itemView.findViewById(R.id.iv_dislike); } } } }
package com.yuqirong.cardswipeview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; /** * @author 五阿哥 */ public class RoundImageView extends ImageView { private Path mPath; private RectF mRectF; private float[] rids = new float[8]; private PaintFlagsDrawFilter paintFlagsDrawFilter; public RoundImageView(Context context) { this(context, null); } public RoundImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView); float mRadius = array.getDimension(R.styleable.RoundImageView_radius, 10); rids[0] = mRadius; rids[1] = mRadius; rids[2] = mRadius; rids[3] = mRadius; rids[4] = 0f; rids[5] = 0f; rids[6] = 0f; rids[7] = 0f; array.recycle(); mPath = new Path(); paintFlagsDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); setLayerType(View.LAYER_TYPE_HARDWARE, null); } @Override protected void onDraw(Canvas canvas) { mPath.reset(); mPath.addRoundRect(mRectF, rids, Path.Direction.CW); canvas.setDrawFilter(paintFlagsDrawFilter); canvas.save(); canvas.clipPath(mPath); super.onDraw(canvas); canvas.restore(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mRectF = new RectF(0, 0, w, h); } }package com.yuqirong.cardswipeview.cardview; /** * @author 五阿哥 */ public final class CardConfig { /** * 显示可见的卡片数量 */ public static final int DEFAULT_SHOW_ITEM = 3; /** * 默认缩放的比例 */ public static final float DEFAULT_SCALE = 0.1f; /** * 卡片Y轴偏移量时按照14等分计算 */ public static final int DEFAULT_TRANSLATE_Y = 14; /** * 卡片滑动时默认倾斜的角度 */ public static final float DEFAULT_ROTATE_DEGREE = 15f; /** * 卡片滑动时不偏左也不偏右 */ public static final int SWIPING_NONE = 1; /** * 卡片向左滑动时 */ public static final int SWIPING_LEFT = 1 << 2; /** * 卡片向右滑动时 */ public static final int SWIPING_RIGHT = 1 << 3; /** * 卡片从左边滑出 */ public static final int SWIPED_LEFT = 1; /** * 卡片从右边滑出 */ public static final int SWIPED_RIGHT = 1 << 2; }
package com.yuqirong.cardswipeview.cardview; import android.graphics.Canvas; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.View; import java.util.List; /** * @author 五阿哥 */ public class CardItemTouchHelperCallback<T> extends ItemTouchHelper.Callback { private final RecyclerView.Adapter adapter; private List<T> dataList; private OnSwipeListener<T> mListener; public CardItemTouchHelperCallback(@NonNull RecyclerView.Adapter adapter, @NonNull List<T> dataList) { this.adapter = checkIsNull(adapter); this.dataList = checkIsNull(dataList); } public CardItemTouchHelperCallback(@NonNull RecyclerView.Adapter adapter, @NonNull List<T> dataList, OnSwipeListener<T> listener) { this.adapter = checkIsNull(adapter); this.dataList = checkIsNull(dataList); this.mListener = listener; } private <T> T checkIsNull(T t) { if (t == null) { throw new NullPointerException(); } return t; } public void setOnSwipedListener(OnSwipeListener<T> mListener) { this.mListener = mListener; } @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = 0; int swipeFlags = 0; RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof CardLayoutManager) { swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; } return makeMovementFlags(dragFlags, swipeFlags); } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { // 移除 onTouchListener,否则触摸滑动会乱了 viewHolder.itemView.setOnTouchListener(null); int layoutPosition = viewHolder.getLayoutPosition(); T remove = dataList.remove(layoutPosition); adapter.notifyDataSetChanged(); if (mListener != null) { mListener.onSwiped(viewHolder, remove, direction == ItemTouchHelper.LEFT ? CardConfig.SWIPED_LEFT : CardConfig.SWIPED_RIGHT); } // 当没有数据时回调 mListener if (adapter.getItemCount() == 0) { if (mListener != null) { mListener.onSwipedClear(); } } } @Override public boolean isItemViewSwipeEnabled() { return false; } @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); View itemView = viewHolder.itemView; if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { float ratio = dX / getThreshold(recyclerView, viewHolder); // ratio 最大为 1 或 -1 if (ratio > 1) { ratio = 1; } else if (ratio < -1) { ratio = -1; } itemView.setRotation(ratio * CardConfig.DEFAULT_ROTATE_DEGREE); int childCount = recyclerView.getChildCount(); // 当数据源个数大于最大显示数时 if (childCount > CardConfig.DEFAULT_SHOW_ITEM) { for (int position = 1; position < childCount - 1; position++) { int index = childCount - position - 1; View view = recyclerView.getChildAt(position); view.setScaleX(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE); view.setScaleY(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE); view.setTranslationY((index - Math.abs(ratio)) * itemView.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y); } } else { // 当数据源个数小于或等于最大显示数时 for (int position = 0; position < childCount - 1; position++) { int index = childCount - position - 1; View view = recyclerView.getChildAt(position); view.setScaleX(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE); view.setScaleY(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE); view.setTranslationY((index - Math.abs(ratio)) * itemView.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y); } } if (mListener != null) { if (ratio != 0) { mListener.onSwiping(viewHolder, ratio, ratio < 0 ? CardConfig.SWIPING_LEFT : CardConfig.SWIPING_RIGHT); } else { mListener.onSwiping(viewHolder, ratio, CardConfig.SWIPING_NONE); } } } } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); viewHolder.itemView.setRotation(0f); } private float getThreshold(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return recyclerView.getWidth() * getSwipeThreshold(viewHolder); } }
package com.yuqirong.cardswipeview.cardview; import android.support.annotation.NonNull; import android.support.v4.view.MotionEventCompat; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; /** * @author 五阿哥 */ public class CardLayoutManager extends RecyclerView.LayoutManager { private RecyclerView mRecyclerView; private ItemTouchHelper mItemTouchHelper; public CardLayoutManager(@NonNull RecyclerView recyclerView, @NonNull ItemTouchHelper itemTouchHelper) { this.mRecyclerView = checkIsNull(recyclerView); this.mItemTouchHelper = checkIsNull(itemTouchHelper); } private <T> T checkIsNull(T t) { if (t == null) { throw new NullPointerException(); } return t; } @Override public RecyclerView.LayoutParams generateDefaultLayoutParams() { return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @Override public void onLayoutChildren(final RecyclerView.Recycler recycler, RecyclerView.State state) { detachAndScrapAttachedViews(recycler); int itemCount = getItemCount(); // 当数据源个数大于最大显示数时 if (itemCount > CardConfig.DEFAULT_SHOW_ITEM) { for (int position = CardConfig.DEFAULT_SHOW_ITEM; position >= 0; position--) { final View view = recycler.getViewForPosition(position); addView(view); measureChildWithMargins(view, 0, 0); int widthSpace = getWidth() - getDecoratedMeasuredWidth(view); int heightSpace = getHeight() - getDecoratedMeasuredHeight(view); // recyclerview 布局 layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2, widthSpace / 2 + getDecoratedMeasuredWidth(view), heightSpace / 2 + getDecoratedMeasuredHeight(view)); if (position == CardConfig.DEFAULT_SHOW_ITEM) { view.setScaleX(1 - (position - 1) * CardConfig.DEFAULT_SCALE); view.setScaleY(1 - (position - 1) * CardConfig.DEFAULT_SCALE); view.setTranslationY((position - 1) * view.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y); } else if (position > 0) { view.setScaleX(1 - position * CardConfig.DEFAULT_SCALE); view.setScaleY(1 - position * CardConfig.DEFAULT_SCALE); view.setTranslationY(position * view.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y); } else { view.setOnTouchListener(mOnTouchListener); } } } else { // 当数据源个数小于或等于最大显示数时 for (int position = itemCount - 1; position >= 0; position--) { final View view = recycler.getViewForPosition(position); addView(view); measureChildWithMargins(view, 0, 0); int widthSpace = getWidth() - getDecoratedMeasuredWidth(view); int heightSpace = getHeight() - getDecoratedMeasuredHeight(view); // recyclerview 布局 layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2, widthSpace / 2 + getDecoratedMeasuredWidth(view), heightSpace / 2 + getDecoratedMeasuredHeight(view)); if (position > 0) { view.setScaleX(1 - position * CardConfig.DEFAULT_SCALE); view.setScaleY(1 - position * CardConfig.DEFAULT_SCALE); view.setTranslationY(position * view.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y); } else { view.setOnTouchListener(mOnTouchListener); } } } } private View.OnTouchListener mOnTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { RecyclerView.ViewHolder childViewHolder = mRecyclerView.getChildViewHolder(v); if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { mItemTouchHelper.startSwipe(childViewHolder); } return false; } }; }
package com.yuqirong.cardswipeview.cardview; import android.support.v7.widget.RecyclerView; /** * @author 五阿哥 */ public interface OnSwipeListener<T> { /** * 卡片还在滑动时回调 * * @param viewHolder 该滑动卡片的viewHolder * @param ratio 滑动进度的比例 * @param direction 卡片滑动的方向,CardConfig.SWIPING_LEFT 为向左滑,CardConfig.SWIPING_RIGHT 为向右滑, * CardConfig.SWIPING_NONE 为不偏左也不偏右 */ void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction); /** * 卡片完全滑出时回调 * * @param viewHolder 该滑出卡片的viewHolder * @param t 该滑出卡片的数据 * @param direction 卡片滑出的方向,CardConfig.SWIPED_LEFT 为左边滑出;CardConfig.SWIPED_RIGHT 为右边滑出 */ void onSwiped(RecyclerView.ViewHolder viewHolder, T t, int direction); /** * 所有的卡片全部滑出时回调 */ void onSwipedClear(); }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.yuqirong.cardswipeview.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="300dp" android:layout_height="400dp" android:background="@drawable/img_card_background" android:gravity="center" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <com.yuqirong.cardswipeview.RoundImageView android:id="@+id/iv_avatar" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" app:radius="7.5dp" android:src="@drawable/img_avatar_01" rel="external nofollow" /> <ImageView android:id="@+id/iv_dislike" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="15dp" android:alpha="0" android:src="@drawable/img_dislike" rel="external nofollow" /> <ImageView android:id="@+id/iv_like" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_marginTop="15dp" android:alpha="0" android:src="@drawable/img_like" rel="external nofollow" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="100dp" android:paddingLeft="14dp" android:paddingTop="15dp"> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="小姐姐" android:textColor="@android:color/black" android:textSize="16sp" /> <TextView android:id="@+id/tv_age" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_name" android:layout_marginTop="5dp" android:background="@drawable/shape_age" android:gravity="center" android:text="♀ 23" android:textColor="#FFFFFF" android:textSize="14sp" /> <TextView android:id="@+id/tv_constellation" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_name" android:layout_marginLeft="4dp" android:layout_marginTop="5dp" android:layout_toRightOf="@id/tv_age" android:background="@drawable/shape_constellation" android:gravity="center" android:text="狮子座" android:textColor="#FFFFFF" android:textSize="14sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_age" android:layout_marginTop="5dp" android:gravity="center" android:text="IT/互联网" android:textColor="#cbcbcb" /> </RelativeLayout> </LinearLayout>
<declare-styleable name="RoundImageView"> <attr name="radius" format="reference|dimension" /> </declare-styleable>
发表评论: