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。