用的是(自定义)RecyclerView实现的仿探探的卡片滑动移除:
首先,布局:
item布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:fresco="http://schemas.android.com/apk/res-auto" android:layout_width="500dp" android:layout_height="700dp" android:orientation="vertical" > <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/cards_sim" android:layout_width="500dp" android:layout_height="600dp" fresco:roundedCornerRadius="15dp" android:layout_gravity="center_horizontal" fresco:placeholderImage="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="500dp" android:layout_height="wrap_content" android:background="#fff" android:orientation="vertical" > <TextView android:id="@+id/cards_title" android:background="@drawable/movietext" //这个movietext,是自定义xml样式布局,下面有 android:layout_width="500dp" android:layout_height="wrap_content" android:textColor="#000" android:textSize="20dp" android:paddingLeft="30dp" android:paddingTop="20dp" android:paddingBottom="20dp" /> </LinearLayout> </LinearLayout>
上面那个注释说的:自定义xml样式布局:
res下,drawable下:创个文件:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape> <stroke android:width="1dp" android:color="#333333" /> <corners android:radius="15dp"/> </shape> </item> </selector>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <android.support.v7.widget.RecyclerView android:id="@+id/cardsmove_recyclerview" android:layout_gravity="center_horizontal" android:layout_width="match_parent" android:layout_height="800dp"> </android.support.v7.widget.RecyclerView> <Button android:id="@+id/btn_splace_cards" android:layout_gravity="center_horizontal" android:layout_marginTop="20dp" android:background="#0f0" android:text="换一批" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
把自定义的布局放入Activity中的View里:
<com.example.jingdong.Find.CardsView.CardsMove //这是自定义View的类名,加入到这里,直接能用,下边会有此类 android:id="@+id/cardsmove_show" android:layout_width="match_parent" android:layout_height="match_parent"> </com.example.jingdong.Find.CardsView.CardsMove>
控制每个item的变量,边距等等常量类:
package com.example.jingdong.Find.CardsView; /** * Created by Administrator on 2017/12/13 0013. */ public 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; }
监视每个item滑动前,滑动后,下一个item的行动,要执行的一些操作,的一个过程类
package com.example.jingdong.Find.CardsView; import android.graphics.Canvas; import android.support.annotation.NonNull; 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 Administrator on 2017/12/13 0013. */ 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; } /** * 设置滑动类型标记 * * @param recyclerView * @param viewHolder * @return * 返回一个整数类型的标识,用于判断Item那种移动行为是允许的 */ @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = 0; int swipeFlags = 0; RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof CardLayoutMannager) { swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; } return makeMovementFlags(dragFlags, swipeFlags); } /** * 拖拽切换Item的回调 * * @param recyclerView * @param viewHolder * @param target * @return * 如果Item切换了位置,返回true;反之,返回false */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; } /** * * 划出时会执行 * @param viewHolder * @param direction:左侧划出为:4,右侧划出为:8 */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { Log.d("mylog", "onSwiped: " + 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(); } } } /** * Item是否支持滑动 * * @return * true 支持滑动操作 * false 不支持滑动操作 */ @Override public boolean isItemViewSwipeEnabled() { return false; } /** * 拖动时会执行的方法 * @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); // Log.d("mylog", "onChildDraw: 拖动"); 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; } Log.d("mylog", "onChildDraw: " + ratio); 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.setScaleX(1 - index * CardConfig.DEFAULT_SCALE ); view.setScaleY(1 - index * 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) { // Log.d("mylog", "onChildDraw: 不为零"); mListener.onSwiping(viewHolder, ratio, ratio < 0 ? CardConfig.SWIPING_LEFT : CardConfig.SWIPING_RIGHT); } else { // Log.d("mylog", "onChildDraw: 为零"); 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); } }
这里是需要自己做一个布局管理者,不能用系统给的布局管理者,item是需要层叠加:
package com.example.jingdong.Find.CardsView; 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; /** * Created by Administrator on 2017/12/13 0013. */ public class CardLayoutMannager extends RecyclerView.LayoutManager{ private RecyclerView mRecyclerView; private ItemTouchHelper mItemTouchHelper; public CardLayoutMannager(@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; } }; }
和上边的一个回调类一起用的,配合使用的一个接口,回调到请求数据的页面,也就是配置RecyclerView的界面:
package com.example.jingdong.Find.CardsView; import android.support.v7.widget.RecyclerView; /** * Created by Administrator on 2017/12/13 0013. */ 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(); }
在这里:数据的Bean类就不放了,下面的网络请求路径给出了,在代码里,自己请求一下,看下数据类型,自己打个Bean类,如果请求路径无效了,自己换个,在下面有的地方需要加入泛型,泛型类型是Bean类,自己复制的时候注意,改一改泛型,不然会没数据
自定义View主页:
package com.example.jingdong.Find.CardsView; import android.content.Context; import android.support.annotation.Nullable; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import com.example.jingdong.R; import com.example.jingdong.RecyclerView.Precenter.RecyclerViewTuijianPrecenter; import com.example.jingdong.RecyclerView.View.RecyclerViewIView; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * Created by Administrator on 2017/12/13 0013. */ public class CardsMove extends LinearLayout implements RecyclerViewIView{
//网络请求路径,下面有给出的随机数方法,最后拼接到路径后面,自己注意一下. String path = "http://api.svipmovie.com/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016&pnum="; RecyclerView rv; Button btn; RecyclerViewAdpt adpt; List<CardsBean.RetBean.ListBean> list; RecyclerViewTuijianPrecenter rp; Context context; public CardsMove(Context context) { this(context,null); } public CardsMove(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); } public CardsMove(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); InitView(context, attrs, defStyleAttr); } private void InitView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this.context = context; View v = View.inflate(context, R.layout.cardsmove,this); rv = v.findViewById(R.id.cardsmove_recyclerview); btn = v.findViewById(R.id.btn_splace_cards); btn.setOnClickListener(new OnClickListener() { //换一换按钮点击事件 @Override public void onClick(View view) { rp.gett(path+getNextPage(),CardsBean.class); } }); rp = new RecyclerViewTuijianPrecenter(); //MVP的P层 rp.attchView(this); //绑定P层接口 rv.setItemAnimator(new DefaultItemAnimator()); //动画效果 list = new ArrayList<>(); adpt = new RecyclerViewAdpt(context,list); //适配器(自己敲就行) rv.setAdapter(adpt); //监视item的行动的回调类 CardItemTouchHelperCallback cc = new CardItemTouchHelperCallback(rv.getAdapter(), list); cc.setOnSwipedListener(new OnSwipeListener<CardsBean.RetBean.ListBean>() { //尖括号中的泛型放
的和适配器的数据类型一样,注意,注意,注意,放入之后,在重写下边的方法 @Override public void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction) { RecyclerViewAdpt.ViewHold vh = (RecyclerViewAdpt.ViewHold) viewHolder; vh.itemView.setAlpha(1 - Math.abs(ratio) * 0.2f); } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, CardsBean.RetBean.ListBean listBean, int direction) { RecyclerViewAdpt.ViewHold vh = (RecyclerViewAdpt.ViewHold) viewHolder; vh.itemView.setAlpha(1f); } @Override public void onSwipedClear() { rv.postDelayed(new Runnable() { @Override public void run() { rp.gett(path+getNextPage(),CardsBean.class); } },3000L); } }); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(cc); //这个我也不懂 CardLayoutMannager cardLayoutMannager = new CardLayoutMannager(rv, itemTouchHelper); //自己定义的管理者 rv.setLayoutManager(cardLayoutMannager); itemTouchHelper.attachToRecyclerView(rv); //这个我也不懂,自己试一试,看用不用加 rp.gett(path+getNextPage(),CardsBean.class); } @Override public void onChengo(Object object) { //请求网络成功回调方法,刷新适配器 Log.e("CardsMove","卡片请求网络成功"); CardsBean cb = (CardsBean) object; List<CardsBean.RetBean.ListBean> t = cb.getRet().getList(); if(t!=null||t.size()!=0){ if(list!=null||list.size()!=0){ list.clear(); } list.addAll(t); adpt.notifyDataSetChanged(); } } @Override public void onShba(Exception exception) { Log.e("CardsMove","卡片请求网络失败"); } private int getNextPage() { //随机数方法 int page = getRandomNumber(1, 108); return page; } public static int getRandomNumber(int min, int max) { return new Random().nextInt(max) % (max - min + 1) + min; } }