热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

RecyclerView实现探探卡片滑动效果

这篇文章主要为大家详细介绍了RecyclerView实现探探卡片滑动效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

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

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


 
 
 
  
 
  
 
  
 
 
 
 
 
  
 
  
 
  
 
  
 
 
 
 

activity_main:

一个常量参数类: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 extends ItemTouchHelper.Callback {
 
 private final RecyclerView.Adapter adapter;
 private List dataList;
 private OnSwipeListener mListener;
 
 public CardItemTouchHelperCallback(@NonNull RecyclerView.Adapter adapter, @NonNull List dataList) {
  this.adapter = checkIsNull(adapter);
  this.dataList = checkIsNull(dataList);
 }
 
 public CardItemTouchHelperCallback(@NonNull RecyclerView.Adapter adapter, @NonNull List dataList, OnSwipeListener listener) {
  this.adapter = checkIsNull(adapter);
  this.dataList = checkIsNull(dataList);
  this.mListener = listener;
 }
 
 private  T checkIsNull(T t) {
  if (t == null) {
   throw new NullPointerException();
  }
  return t;
 }
 
 public void setOnSwipedListener(OnSwipeListener 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 &#63; 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 

自定义布局管理器: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 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 {
 
 /**
  * 卡片还在滑动时回调
  *
  * @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 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() {
 
   @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 &#63; "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:


 
  
 

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


推荐阅读
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 本文详细介绍了如何使用Spring Boot进行高效开发,涵盖了配置、实例化容器以及核心注解的使用方法。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
author-avatar
顺大顺麻麻1009_388
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有