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

Android App中ListView仿QQ实现滑动删除效果的要点解析

程序员文章站 2024-02-29 09:33:16
本来准备在listview的每个item的布局上设置一个隐藏的button,当滑动的时候显示。但是因为每次只要存在一个button,发现每个item上的button相互间不...

本来准备在listview的每个item的布局上设置一个隐藏的button,当滑动的时候显示。但是因为每次只要存在一个button,发现每个item上的button相互间不好控制。所以决定继承listview然后结合popupwindow。
首先是布局文件:
delete_btn.xml:这里只需要一个button

<?xml version="1.0" encoding="utf-8"?> 
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:orientation="vertical" > 
   <button  
    android:id="@+id/id_item_btn" 
    android:layout_width="60dp" 
    android:singleline="true" 
    android:layout_height="wrap_content" 
    android:text="删除" 
    android:background="@drawable/d_delete_btn" 
    android:textcolor="#ffffff" 
    android:paddingleft="15dp" 
    android:paddingright="15dp" 
    android:layout_alignparentright="true" 
    android:layout_centervertical="true" 
    android:layout_marginright="15dp" 
    /> 
</linearlayout> 

主布局文件:activity_main.xml,listview的每个item的样式直接使用了系统的android.r.layout.simple_list_item_1

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" > 
 
  <com.example.listviewitemslidedeletebtnshow.qqlistview 
    android:id="@+id/id_listview" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" > 
  </com.example.listviewitemslidedeletebtnshow.qqlistview> 
 
</relativelayout> 

接下来看看qqlistview的实现:

package com.example.listviewitemslidedeletebtnshow; 
 
import android.content.context; 
import android.util.attributeset; 
import android.view.gravity; 
import android.view.layoutinflater; 
import android.view.motionevent; 
import android.view.view; 
import android.view.viewconfiguration; 
import android.widget.button; 
import android.widget.linearlayout; 
import android.widget.listview; 
import android.widget.popupwindow; 
 
public class qqlistview extends listview 
{ 
 
  private static final string tag = "qqlistview"; 
 
  // private static final int velocity_sanp = 200; 
  // private velocitytracker mvelocitytracker; 
  /** 
   * 用户滑动的最小距离 
   */ 
  private int touchslop; 
 
  /** 
   * 是否响应滑动 
   */ 
  private boolean issliding; 
 
  /** 
   * 手指按下时的x坐标 
   */ 
  private int xdown; 
  /** 
   * 手指按下时的y坐标 
   */ 
  private int ydown; 
  /** 
   * 手指移动时的x坐标 
   */ 
  private int xmove; 
  /** 
   * 手指移动时的y坐标 
   */ 
  private int ymove; 
 
  private layoutinflater minflater; 
 
  private popupwindow mpopupwindow; 
  private int mpopupwindowheight; 
  private int mpopupwindowwidth; 
 
  private button mdelbtn; 
  /** 
   * 为删除按钮提供一个回调接口 
   */ 
  private delbuttonclicklistener mlistener; 
 
  /** 
   * 当前手指触摸的view 
   */ 
  private view mcurrentview; 
 
  /** 
   * 当前手指触摸的位置 
   */ 
  private int mcurrentviewpos; 
 
  /** 
   * 必要的一些初始化 
   * 
   * @param context 
   * @param attrs 
   */ 
  public qqlistview(context context, attributeset attrs) 
  { 
    super(context, attrs); 
 
    minflater = layoutinflater.from(context); 
    touchslop = viewconfiguration.get(context).getscaledtouchslop(); 
 
    view view = minflater.inflate(r.layout.delete_btn, null); 
    mdelbtn = (button) view.findviewbyid(r.id.id_item_btn); 
    mpopupwindow = new popupwindow(view, linearlayout.layoutparams.wrap_content, 
        linearlayout.layoutparams.wrap_content); 
    /** 
     * 先调用下measure,否则拿不到宽和高 
     */ 
    mpopupwindow.getcontentview().measure(0, 0); 
    mpopupwindowheight = mpopupwindow.getcontentview().getmeasuredheight(); 
    mpopupwindowwidth = mpopupwindow.getcontentview().getmeasuredwidth(); 
  } 
 
  @override 
  public boolean dispatchtouchevent(motionevent ev) 
  { 
    int action = ev.getaction(); 
    int x = (int) ev.getx(); 
    int y = (int) ev.gety(); 
    switch (action) 
    { 
 
    case motionevent.action_down: 
      xdown = x; 
      ydown = y; 
      /** 
       * 如果当前popupwindow显示,则直接隐藏,然后屏蔽listview的touch事件的下传 
       */ 
      if (mpopupwindow.isshowing()) 
      { 
        dismisspopwindow(); 
        return false; 
      } 
      // 获得当前手指按下时的item的位置 
      mcurrentviewpos = pointtoposition(xdown, ydown); 
      // 获得当前手指按下时的item 
      view view = getchildat(mcurrentviewpos - getfirstvisibleposition()); 
      mcurrentview = view; 
      break; 
    case motionevent.action_move: 
      xmove = x; 
      ymove = y; 
      int dx = xmove - xdown; 
      int dy = ymove - ydown; 
      /** 
       * 判断是否是从右到左的滑动 
       */ 
      if (xmove < xdown && math.abs(dx) > touchslop && math.abs(dy) < touchslop) 
      { 
        // log.e(tag, "touchslop = " + touchslop + " , dx = " + dx + 
        // " , dy = " + dy); 
        issliding = true; 
      } 
      break; 
    } 
    return super.dispatchtouchevent(ev); 
  } 
 
  @override 
  public boolean ontouchevent(motionevent ev) 
  { 
    int action = ev.getaction(); 
    /** 
     * 如果是从右到左的滑动才相应 
     */ 
    if (issliding) 
    { 
      switch (action) 
      { 
      case motionevent.action_move: 
 
        int[] location = new int[2]; 
        // 获得当前item的位置x与y 
        mcurrentview.getlocationonscreen(location); 
        // 设置popupwindow的动画 
        mpopupwindow.setanimationstyle(r.style.popwindow_delete_btn_anim_style); 
        mpopupwindow.update(); 
        mpopupwindow.showatlocation(mcurrentview, gravity.left | gravity.top, 
            location[0] + mcurrentview.getwidth(), location[1] + mcurrentview.getheight() / 2 
                - mpopupwindowheight / 2); 
        // 设置删除按钮的回调 
        mdelbtn.setonclicklistener(new onclicklistener() 
        { 
          @override 
          public void onclick(view v) 
          { 
            if (mlistener != null) 
            { 
              mlistener.clickhappend(mcurrentviewpos); 
              mpopupwindow.dismiss(); 
            } 
          } 
        }); 
        // log.e(tag, "mpopupwindow.getheight()=" + mpopupwindowheight); 
 
        break; 
      case motionevent.action_up: 
        issliding = false; 
 
      } 
      // 相应滑动期间屏幕itemclick事件,避免发生冲突 
      return true; 
    } 
 
    return super.ontouchevent(ev); 
  } 
 
  /** 
   * 隐藏popupwindow 
   */ 
  private void dismisspopwindow() 
  { 
    if (mpopupwindow != null && mpopupwindow.isshowing()) 
    { 
      mpopupwindow.dismiss(); 
    } 
  } 
 
  public void setdelbuttonclicklistener(delbuttonclicklistener listener) 
  { 
    mlistener = listener; 
  } 
 
  interface delbuttonclicklistener 
  { 
    public void clickhappend(int position); 
  } 
 
} 

代码注释写得很详细,简单说一下,在dispatchtouchevent中设置当前是否响应用户滑动,然后在ontouchevent中判断是否响应,如果响应则popupwindow以动画的形式展示出来。当然屏幕上如果存在popupwindow则屏幕listview的滚动与item的点击,以及从右到左滑动时屏幕item的click事件。
接下来是mainactivity.java,这里代码很简单不做介绍了。

package com.example.listviewitemslidedeletebtnshow; 
 
import java.util.arraylist; 
import java.util.arrays; 
import java.util.list; 
 
import android.app.activity; 
import android.os.bundle; 
import android.view.view; 
import android.widget.adapterview; 
import android.widget.adapterview.onitemclicklistener; 
import android.widget.arrayadapter; 
import android.widget.toast; 
 
import com.example.listviewitemslidedeletebtnshow.qqlistview.delbuttonclicklistener; 
 
public class mainactivity extends activity 
{ 
  private qqlistview mlistview; 
  private arrayadapter<string> madapter; 
  private list<string> mdatas; 
 
  @override 
  protected void oncreate(bundle savedinstancestate) 
  { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.activity_main); 
 
    mlistview = (qqlistview) findviewbyid(r.id.id_listview); 
    // 不要直接arrays.aslist 
    mdatas = new arraylist<string>(arrays.aslist("helloworld", "welcome", "java", "android", "servlet", "struts", 
        "hibernate", "spring", "html5", "javascript", "lucene")); 
    madapter = new arrayadapter<string>(this, android.r.layout.simple_list_item_1, mdatas); 
    mlistview.setadapter(madapter); 
 
    mlistview.setdelbuttonclicklistener(new delbuttonclicklistener() 
    { 
      @override 
      public void clickhappend(final int position) 
      { 
        toast.maketext(mainactivity.this, position + " : " + madapter.getitem(position), 1).show(); 
        madapter.remove(madapter.getitem(position)); 
      } 
    }); 
 
    mlistview.setonitemclicklistener(new onitemclicklistener() 
    { 
      @override 
      public void onitemclick(adapterview<?> parent, view view, int position, long id) 
      { 
        toast.maketext(mainactivity.this, position + " : " + madapter.getitem(position), 1).show(); 
      } 
    }); 
  } 
} 

效果图如下:楼主使用asm.jar以及gifcamera截的gif,由于button的动画很短感觉截图效果很卡不流畅,大家有什么好的截图,还望推荐。有兴趣的还是下载源码看看效果i。

Android App中ListView仿QQ实现滑动删除效果的要点解析