欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android仿QQ列表左滑删除操作

程序员文章站 2024-02-27 19:52:51
最近学习了如何做一个像qq的左滑recyclerview的item显示选项的,主要是用到scroller 我们首先新建一个自己的recyclerview 定义好一些要用...

最近学习了如何做一个像qq的左滑recyclerview的item显示选项的,主要是用到scroller

我们首先新建一个自己的recyclerview

定义好一些要用的的变量
重写构造方法,把前两个构造方法改为如下,使无论如何构造都要执行第三个构造方法
在第三个构造方法里初始化scroller

public class leftswipemenurecyclerview extends recyclerview {

  //置顶按钮
  private textview tvtop;
  //删除按钮
  private textview tvdelete;
  //item相应的布局
  private linearlayout mitemlayout;
  //菜单的最大宽度
  private int mmaxlength;

  //上一次触摸行为的x坐标
  private int mlastx;
  //上一次触摸行为的y坐标
  private int mlasty;

  //当前触摸的item的位置
  private int mposition;

  //是否在垂直滑动列表
  private boolean isdragging;
  //item是在否跟随手指移动
  private boolean isitemmoving;
  //item是否开始自动滑动
  private boolean isstartscroll;

  //左滑菜单状态  0:关闭 1:将要关闭 2:将要打开 3:打开
  private int mmenustate;
  private static int menu_closed = 0;
  private static int menu_will_closed = 1;
  private static int menu_open = 2;
  private static int menu_will_open = 3;

  //实现弹性滑动,恢复
  private scroller mscroller;

  //item的事件监听
  private onitemactionlistener mlistener;

  public leftswipemenurecyclerview(context context) {
    this(context, null);
  }

  public leftswipemenurecyclerview(context context, @nullable attributeset attrs) {
    this(context, attrs, 0);
  }

  public leftswipemenurecyclerview(context context, @nullable attributeset attrs, int defstyle) {
    super(context, attrs, defstyle);
    mscroller = new scroller(context, new linearinterpolator());
  }

重写ontouchevent方法

event主要有以下几个action

  • action_down 手指接触到屏幕
  • action_move 手指在屏幕滑动
  • action_up 手指离开屏幕

一开始肯定要获取x和y的相对坐标

int x= (int) event.getx();
int y= (int) event.gety();

然后接下来分别处理3个不同的行为

1.action_down

case motionevent.action_down:
        if (mmenustate == menu_closed) {
          //根据坐标获得view
          view view = findchildviewunder(x, y);
          if (view == null) {
            return false;
          }
          //获得这个view的viewholder
          rvadapter.holder holder = (rvadapter.holder) getchildviewholder(view);
          //获得这个view的position
          mposition = holder.getadapterposition();
          //获得这个view的整个布局
          mitemlayout = holder.lllayout;
          //获得这个view的删除按钮
          tvdelete = holder.tvdelete;
          //这个view的整个置顶按钮
          tvtop = holder.tvtop;
          //两个按钮的宽度
          mmaxlength = tvdelete.getwidth() + tvtop.getwidth();

          //设置删除按钮点击监听
          tvdelete.setonclicklistener(new onclicklistener() {
            @override
            public void onclick(view view) {
              mitemlayout.scrollto(0, 0);
              mmenustate =menu_closed;
              mlistener.onitemdelete(mposition);
            }
          });
          //设置置顶按钮点击监听
          tvtop.setonclicklistener(new onclicklistener() {
            @override
            public void onclick(view view) {
              mitemlayout.scrollto(0, 0);
              mmenustate =menu_closed;
              mlistener.onitemtop(mposition);
            }
          });
          //如果是打开状态,点击其他就把当前menu关闭掉
        } else if (mmenustate == menu_open) {
          mscroller.startscroll(mitemlayout.getscrollx(), 0, -mmaxlength, 0, 200);
          invalidate();
          mmenustate = menu_closed;
          //该点击无效
          return false;
        } else {
          return false;
        }
        break;

2.action_move

      case motionevent.action_move:
        //计算偏移量
        int dx = mlastx - x;
        int dy = mlasty - y;
        //当前滑动的x
        int scrollx = mitemlayout.getscrollx();

        if (math.abs(dx) > math.abs(dy)) {

          isitemmoving = true;
          //超出左边界则始终保持不动
          if (scrollx + dx <= 0) {
            mitemlayout.scrollto(0, 0);
            //滑动无效
            return false;
          //超出右边界则始终保持不动
          } else if (scrollx + dx >= mmaxlength) {
            mitemlayout.scrollto(mmaxlength, 0);
            //滑动无效
            return false;
          }
          //菜单随着手指移动
          mitemlayout.scrollby(dx, 0);
        //如果水平移动距离大于30像素的话,recyclerview不会上下滑动
        }else if (math.abs(dx) > 30){
          return true;
        }
        //如果菜单正在打开就不能上下滑动
        if (isitemmoving){
          mlastx = x;
          mlasty = y;
          return true;
        }
        break;

3.action_up

case motionevent.action_up:
        //手指抬起时判断是否点击,静止且有listener才能点击
        if (!isitemmoving && !isdragging && mlistener != null) {
          mlistener.onitemclick(mposition);
        }
        isitemmoving = false;

        //等一下要移动的距离
        int deltax = 0;
        int upscrollx = mitemlayout.getscrollx();
        //滑动距离大于1/2menu长度就自动展开,否则就关掉
        if (upscrollx >= mmaxlength / 2) {
          deltax = mmaxlength - upscrollx;
          mmenustate = menu_will_open;
        } else if (upscrollx < mmaxlength / 2) {
          deltax = -upscrollx;
          mmenustate = menu_will_closed;
        }
        //知道我们为什么不直接把mmenustate赋值为menu_open或者menu_closed吗?因为滑动时有时间的,我们可以在滑动完成时才把状态改为已经完成
        mscroller.startscroll(upscrollx, 0, deltax, 0, 200);
        isstartscroll = true;
        //刷新界面
        invalidate();
        break;

之后还要在ontouchevent方法里刷新坐标

   //只有更新mlastx,mlasty数据才会准确
    mlastx = x;
    mlasty = y;
    return super.ontouchevent(e);

因为我们用到了startscroll所以要重写computescroll方法

  public void computescroll() {
    //判断scroller是否完成滑动
    if (mscroller.computescrolloffset()) {
      mitemlayout.scrollto(mscroller.getcurrx(), mscroller.getcurry());
      //这个很重要
      invalidate();
    //如果已经完成就改变状态
    } else if (isstartscroll) {
      isstartscroll = false;
      if (mmenustate == menu_will_closed) {
        mmenustate = menu_closed;
      }
      if (mmenustate == menu_will_open) {
        mmenustate = menu_open;
      }
    }
  }

**还要监听recyclerview是否在上下滑动

 @override
  public void onscrollstatechanged(int state) {
    super.onscrollstatechanged(state);
    //是否在上下滑动
    isdragging = state == scroll_state_dragging;
  }

还要设置listener

//设置listener
  public void setonitemactionlistener(onitemactionlistener onitemactionlistener) {
    this.mlistener = onitemactionlistener;
  }

这个listener是要自己新建的

public interface onitemactionlistener {
  void onitemclick(int position);
  void onitemtop(int position);
  void onitemdelete(int position);
}

最后是点击,置顶,删除在activity里的回调

这里只展示回调实现部分,我这里用的list是linkedlist,可以在第一位添加数据

rv.setonitemactionlistener(new onitemactionlistener() {
      //点击
      @override
      public void onitemclick(int position) {
        toast.maketext(mainactivity.this,"click"+position,toast.length_short).show();
      }
      //置顶
      @override
      public void onitemtop(int position) {
        //获得当前位置的内容
        string temp =list.get(position);
        //移除这个item
        list.remove(position);
        //把它添加到第一个
        list.addfirst(temp);
        //更新数据源
        adapter.notifydatasetchanged();
      }
      //删除
      @override
      public void onitemdelete(int position) {
        list.remove(position);
        //更新数据源
        adapter.notifydatasetchanged();
      }
    });

adapter和布局的代码太简单我就不放出来了,大家可以到源码里看看有什么

效果图

Android仿QQ列表左滑删除操作

源码地址:https://github.com/jkgeekjack/slideleftmenurecyclerview

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