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列表信息滑动删除功能,希望对大家有所帮助