当前位置: 移动技术网 > IT编程>移动开发>Android > RecyclerView实现探探卡片滑动效果

RecyclerView实现探探卡片滑动效果

2019年07月30日  | 移动技术网IT编程  | 我要评论

皮蛋网,爱火车仁表,三维数字化建模

这里是一个通过自定义view和自定义recyclerview的:layoutmanager,再结合itemtouchhelper实现的一个仿探探的卡片滑动的效果:

效果图已经奉上,接下来是代码:

首先是每张图片的布局:item

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="336dp"
 android:layout_height="426dp"
 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.bwie.w.test1121.cardswipelayout.roundimageview
   android:id="@+id/iv_avatar"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:scaletype="centercrop"
   android:src="@drawable/img_avatar_01"
   app:radius="7.5dp" />
 
  <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" />
 
  <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" />
 
 </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>

activity_main:

<android.support.v7.widget.recyclerview
  android:id="@+id/recyclerview"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

一个常量参数类:cardconfig

/**
 * 常量参数
 */
 
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;
}

拖动item的回调类:carditemtouchhelpercallback

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 cardlayoutmanager) {
   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);
 }
 
}

自定义布局管理器:cardlayoutmanager:

/**
 * 自定义布局管理器
 */
 
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;
  }
 };
 
}

状态回调接口:onswipelistener

/**
 * @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();
 
}

自定义条目图片样式:roundimageview:

/**
 * 自定义图片样式,顶部圆角显示
 */
 
public class roundimageview extends imageview {
 
 private path mpath;
 private rectf mrectf;
 /*圆角的半径,依次为左上角xy半径,右上角,右下角,左下角*/
 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) {
  // log.d("mylog", "ondraw: ");
  //重置path
  mpath.reset();
  //p1:大小,p2:圆角,p3:cw:顺时针绘制path,ccw:逆时针
  mpath.addroundrect(mrectf, rids, path.direction.cw);
  //添加抗锯齿
  canvas.setdrawfilter(paintflagsdrawfilter);
  canvas.save();
  //该方法不支持硬件加速,如果开启会导致效果出不来,所以之前设置关闭硬件加速
  //clip(剪切)的时机:通常理解的clip(剪切),是对已经存在的图形进行clip的。
  // 但是,在android上是对canvas(画布)上进行clip的,要在画图之前对canvas进行clip,
  // 如果画图之后再对canvas进行clip不会影响到已经画好的图形。一定要记住clip是针对canvas而非图形
  //开始根据path裁剪
  canvas.clippath(mpath);
  super.ondraw(canvas);
  canvas.restore();
 }
 
 int a,b;
 //执行在ondraw()之前
 @override
 protected void onsizechanged(int w, int h, int oldw, int oldh) {
  super.onsizechanged(w, h, oldw, oldh);
  // log.d("mylog", "onsizechanged: ");
  a = w;
  b = h;
  mrectf = new rectf(0, 0, w, h);
  log.d("mylog", "onsizechanged: "+w+"-----"+h);
 }
 
}

mainactivity:

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 = 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);
   }
 
  }
 }
}

attrs:

<resources>
 <declare-styleable name="roundimageview">
  <attr name="radius" format="reference|dimension" />
 </declare-styleable>
</resources>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网