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

Android开发中模仿qq列表信息滑动删除功能

程序员文章站 2024-02-20 10:54:59
这个效果的完成主要分为两个部分 自定义view作为listview的列表项 一个view里面包括 显示头像,名字,消息内容等的contentview和滑动才能显示出来...

Android开发中模仿qq列表信息滑动删除功能

这个效果的完成主要分为两个部分

自定义view作为listview的列表项 一个view里面包括 显示头像,名字,消息内容等的contentview和滑动才能显示出来的删除,置顶的右边菜单menuview 在手指移动的时候同时改变这两个视图的位置

重写listview 判断item向左还是向右滑动 正常的滚动还是左右滑动等等 重写ontouchevent 进行事件分发

大致思路:

listview进行事件分发,判断需要滑动还是滚动等状态,如果需要滑动将事件传递给item进行滑动处理. 在item中控制contentview和menuview进行位置的变化完成滚动效果

重写listview代码

public class slidelistview extends listview{
  private slideitem mtouchview=null;//记录当前点击的item view
  private float mdownx;//x轴坐标
  private float mdowny;//y轴坐标
  private int mtouchstate;//记录点击状态
  private int mtouchposition;//记录点击位置
  private static final int touch_state_none=0; //按下状态
  private static final int touch_state_x=1;//横滑状态
  private static final int touch_state_y=2;//竖滑状态
  //判断横竖滑动的最小值
  private static final int max_y=5;
  private static final int max_x=3;
  public slidelistview(context context) {
    super(context);
  }
  public slidelistview(context context, attributeset attrs) {
    super(context, attrs);
  }
  public slidelistview(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
  }
  @override
  public boolean ontouchevent(motionevent ev) {
    if (ev.getaction() != motionevent.action_down && mtouchview == null)
      return super.ontouchevent(ev);
    switch (ev.getaction()) {
      case motionevent.action_down:
        //按住的item的position
        int oldposition = mtouchposition;
        //记录位置
        mdownx = ev.getx();
        mdowny = ev.gety();
        mtouchstate = touch_state_none;
        //根据当前横纵坐标点获取点击的item的position
        mtouchposition = this.pointtoposition((int) ev.getx(), (int) ev.gety());
        //判断当前点击的是否和上次点击的item是同一个,如果是同一个,并且状态是打开了的就记录状态和坐标
        //记录坐标通过item中的downx属性
        if (mtouchposition == oldposition && mtouchview != null && mtouchview.isopen()) {
          mtouchstate = touch_state_x;
          mtouchview.onswipe(ev);
          return true;
        }
        //获取当前的item的view
        view currentview = getchildat(mtouchposition - getfirstvisibleposition());
        //如果不是同一个item 那么点击的话就关闭掉之前打开的item
        if (mtouchview != null && mtouchview.isopen()) {
          mtouchview.smoothclosemenu();
          mtouchview = null;
          return super.ontouchevent(ev);
        }
        //判断该view的类型
        if (currentview instanceof slideitem) {
          mtouchview = (slideitem) currentview;
        }
        if (mtouchview != null) {
          mtouchview.onswipe(ev);
        }
        break;
      case motionevent.action_move:
        float dy = math.abs((ev.gety() - mdowny));
        float dx = math.abs((ev.getx() - mdownx));
        if (mtouchstate == touch_state_x) {
          if (mtouchview != null) {
            //执行滑动
            mtouchview.onswipe(ev);
          }
          return true;
        } else if (mtouchstate == touch_state_none) {
          //判断滑动方向,x方向执行滑动,y方向执行滚动
          if (math.abs(dy) > max_y) {
            mtouchstate = touch_state_y;
          } else if (dx > max_x) {
            mtouchstate = touch_state_x;
          }
        }
        break;
      case motionevent.action_up:
        //判断状态
        if (mtouchstate == touch_state_x) {
          if (mtouchview != null) {
            mtouchview.onswipe(ev);
            //如过最后状态是打开 那么就重新初始化
            if (!mtouchview.isopen()) {
              mtouchposition = -1;
              mtouchview = null;
            }
          }
          ev.setaction(motionevent.action_cancel);
          super.ontouchevent(ev);
          return true;
        }
        break;
    }
    return super.ontouchevent(ev);
  }
}

重写item项

view的滑动效果都是在里完成的 使用了scroller类

关于scroller的使用文章最后已经粘出了大神的帖子 不懂的同学可以先把scroller的使用理解了在看这个滑动效果就很好懂了 我在这里简单讲讲

这个类的并没有实际的完成滚动效果 它是一个计算控件移动轨迹的辅助类,
比如说:在1秒内从位置0移动到位置100 这个类会计算出移动的数值,它并没有完成滑动的效果,但是告诉了我们这个滑动的过程 实际的上的view移动操作在computescroll()完成 这个方法是view的自带方法 需要我们重写

computescroll方法又是怎么情况呢 看源码 本身是个空的 就等着我们实现 我们实际改变view位置的代码就是在此方法内调用的

额。。。英语一般

大致意思 我们要通过scroller实现一个滚动效果的时候 父布局就会调用此方法来完成子视图的位置更新

官方的描述是:当我们执行ontouch或invalidate()或postinvalidate()都会导致这个方法的执行

在此方法中不断的获取到移动的距离 通过view自带的layout()方法更新view所在位置

 /**
   * called by a parent to request that a child update its values for mscrollx
   * and mscrolly if necessary. this will typically be done if the child is
   * animating a scroll using a {@link android.widget.scroller scroller}
   * object.
   */
  public void computescroll() {
  }
public class slideitem extends linearlayout {
  private view contentview = null; //不滑动显示的view
  private view menuview = null; //左滑显示的view
  //计算滑动 动画效果
  private scroller mopenscroller;
  private scroller mclosescroller;
  private int downx; //开始按下的位置
  //记录状态
  private int state = state_close;
  private static final int state_close = 0;
  private static final int state_open = 1;
  private int mbasex;//在关闭滑动的时候计算与父布局的剩余距离
  public slideitem(context context) {
    super(context);
  }
  public slideitem(context context, attributeset attrs) {
    super(context, attrs);
  }
  public slideitem(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
  }
  public void setcontentview(view contentview, view rightview){
    this.contentview = contentview;
    this.menuview = rightview;
    //初始化mcolosescroller和mopenscroller
    mclosescroller=new scroller(getcontext());
    mopenscroller = new scroller(getcontext());
    initview();
  }
  //child view的布局参数设定好后 添加到parent view里面
  private void initview() {
    //这是设置宽和高
    layoutparams contentparams = new layoutparams
        (layoutparams.match_parent, layoutparams.match_parent);
    layoutparams rightparams=new layoutparams
        (layoutparams.wrap_content, layoutparams.match_parent);
    contentview.setlayoutparams(contentparams);
    contentview.setpadding(10,10,10,10);
    menuview.setlayoutparams(rightparams);
    this.addview(contentview);
    this.addview(menuview);
  }
  // 判断是否滑出的状态
  public boolean isopen() {
    return state == state_open;
  }
  /**
   * 供listview调用 进行视图的移动  listview判断状态 什么情况下左滑
   * @param event
   * @return
   */
  public boolean onswipe(motionevent event) {
    switch (event.getaction()) {
      case motionevent.action_down:
        downx = (int) event.getx();
        break;
      case motionevent.action_move:
        //按下位置减去移动位置 获取移动的距离
        int dis = (int) (downx - event.getx());
        if (state == state_open) {
          dis += menuview.getwidth();
        }
        //移动
        move(dis);
        break;
      case motionevent.action_up:
        //当滑到右边视图一半的距离 自动滑进滑出
        if ((downx - event.getx()) > (menuview.getwidth() / 2)) {
          smoothopenmenu();
        } else {
          smoothclosemenu();
          return false;
        }
        break;
    }
    //消费掉事件
    return true;
  }
  /**
   * 视图重新绘制时调用
   */
  @override
  public void computescroll() {
    if (state == state_open) {
      //computescrolloffset滑动是否结束
      if (mopenscroller.computescrolloffset()) {
        move(mopenscroller.getcurrx());
        postinvalidate();
      }
    } else {
      if (mclosescroller.computescrolloffset()) {
        move(mbasex - mclosescroller.getcurrx());
        postinvalidate();
      }
    }
  }
  /**
   * 移动视图
   * @param dis
   */
  private void move(int dis) {
    //这两个判断是为了保证 不要把视图移动过多 导致视图偏移
    if (dis > menuview.getwidth()) {
      dis = menuview.getwidth();
    }
    if (dis < 0) {
      dis = 0;
    }
    //view.layout()控制view相对于其父布局的位置  在触发移动的时候调用不断改变位置 完成实际的滑动效果
    contentview.layout(-dis, contentview.gettop(), contentview.getwidth() - dis, getmeasuredheight());
    menuview.layout(contentview.getwidth() - dis, menuview.gettop(), contentview.getwidth() + menuview.getwidth() - dis, menuview.getbottom());
  }
  /**
   * 滑动关闭
   * contentview.getleft() 与其父视图的相对位置
   */
  public void smoothclosemenu() {
    state = state_close;
    mbasex = -contentview.getleft();
    mclosescroller.startscroll(0, 0, mbasex, 0, 350);
    postinvalidate();
  }
  /**
   * 滑动打开
   */
  public void smoothopenmenu() {
    state = state_open;
    mopenscroller.startscroll(-contentview.getleft(), 0, menuview.getwidth(), 0, 350);
    postinvalidate();
  }
  @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    super.onmeasure(widthmeasurespec, heightmeasurespec);
    if(menuview != null)
      menuview.measure(measurespec.makemeasurespec(0, measurespec.unspecified),
          measurespec.makemeasurespec(getmeasuredheight(), measurespec.exactly));
  }
  @override
  protected void onlayout(boolean changed, int l, int t, int r, int b) {
    //确保centerview menuview的显示位置
    if(contentview != null)
      contentview.layout(0, 0, getmeasuredwidth(), contentview.getmeasuredheight());
    if(menuview != null)
      menuview.layout(getmeasuredwidth(), 0, getmeasuredwidth() + menuview.getmeasuredwidth(), contentview.getmeasuredheight());
  }
}

适配器

public class slideadapter extends baseadapter implements view.onclicklistener{
  private list<string> datalist;
  private context context;
  private layoutinflater inflater;
  public slideadapter(context context, list<string> datalist) {
    this.context = context;
    this.datalist = datalist;
    this.inflater=layoutinflater.from(context);
  }
  @override
  public int getcount() {
    return 5;
  }
  @override
  public object getitem(int position) {
    return null;
  }
  @override
  public long getitemid(int position) {
    return 0;
  }
  @override
  public view getview(int position, view convertview, viewgroup parent) {
    viewholder holder=null;
    if (convertview==null){
      view content=inflater.inflate(r.layout.adapter_item_content,null);
      view menu=inflater.inflate(r.layout.adapter_item_menu,null);
      holder=new viewholder(content,menu);
      slideitem slideitem=new slideitem(context);
      slideitem.setcontentview(content,menu);
      convertview=slideitem;
      convertview.settag(holder);
    }else {
      holder= (viewholder) convertview.gettag();
    }
    holder.itemtvdelete.setonclicklistener(this);
    holder.itemtvnoread.setonclicklistener(this);
    holder.itemtvtotop.setonclicklistener(this);
    return convertview;
  }
  class viewholder{
    textview itemtvtotop;
    textview itemtvnoread;
    textview itemtvdelete;
    public viewholder(view center,view menu) {
      this.itemtvtotop = (textview) menu.findviewbyid(r.id.item_to_top);
      this.itemtvnoread = (textview) menu.findviewbyid(r.id.item_no_read);
      this.itemtvdelete = (textview) menu.findviewbyid(r.id.item_delete);
    }
  }
  @override
  public void onclick(view v) {
    switch (v.getid()){
      case r.id.item_no_read:
        toast.maketext(context,"标为未读",toast.length_short).show();
        break;
      case r.id.item_to_top:
        toast.maketext(context,"置顶了熬",toast.length_short).show();
        break;
      case r.id.item_delete:
        toast.maketext(context,"删除啦",toast.length_short).show();
        break;
    }
  }
}

参考文档:

swipemenulistview github上的实现此效果的开源项目

以上所述是小编给大家介绍的android开发中模仿qq列表信息滑动删除功能,希望对大家有所帮助