android实现QQ微信侧滑删除效果
程序员文章站
2022-03-08 13:25:27
最近由于项目需求,需要做一个listview中的item策划删除的效果,与是查找资料和参考了一些相关的博客,终于完美实现了策划删除的效果。
先看一下效果图(研究了半天...
最近由于项目需求,需要做一个listview中的item策划删除的效果,与是查找资料和参考了一些相关的博客,终于完美实现了策划删除的效果。
先看一下效果图(研究了半天竟然没研究出来真机上gif图怎么做,大家将就看一下吧)。
侧滑效果图
点击删除后的截图
点击删除后,listview中的第一个“微信团队”被删除。
接下来看代码部分,很多注释都在代码中,直接上代码。
首先自定义个slideview继承linearlayout。
import android.util.attributeset; import android.util.log; import android.util.typedvalue; import android.view.motionevent; import android.view.view; import android.widget.linearlayout; import android.widget.relativelayout; import android.widget.scroller; import android.widget.textview; /** * slideview 继承自linearlayout */ public class slideview extends linearlayout { private static final string tag = "slideview"; private context mcontext; // 用来放置所有view的容器 private linearlayout mviewcontent; // 用来放置内置view的容器,比如删除 按钮 private relativelayout mholder; // 弹性滑动对象,提供弹性滑动效果 private scroller mscroller; // 滑动回调接口,用来向上层通知滑动事件 private onslidelistener monslidelistener; // 内置容器的宽度 单位:dp private int mholderwidth = 120; // 分别记录上次滑动的坐标 private int mlastx = 0; private int mlasty = 0; // 用来控制滑动角度,仅当角度a满足如下条件才进行滑动:tan a = deltax / deltay > 2 private static final int tan = 2; public interface onslidelistener { // slideview的三种状态:开始滑动,打开,关闭 public static final int slide_status_off = 0; public static final int slide_status_start_scroll = 1; public static final int slide_status_on = 2; public void onslide(view view, int status); } public slideview(context context) { super(context); initview(); } public slideview(context context, attributeset attrs) { super(context, attrs); initview(); } private void initview() { mcontext = getcontext(); // 初始化弹性滑动对象 mscroller = new scroller(mcontext); // 设置其方向为横向 setorientation(linearlayout.horizontal); // 将slide_view_merge加载进来 view.inflate(mcontext, r.layout.slide_view_merge, this); mviewcontent = (linearlayout) findviewbyid(r.id.view_content); mholderwidth = math.round(typedvalue.applydimension( typedvalue.complex_unit_dip, mholderwidth, getresources() .getdisplaymetrics())); } // 设置按钮的内容,也可以设置图标啥的,我没写 public void setbuttontext(charsequence text) { ((textview) findviewbyid(r.id.delete)).settext(text); } // 将view加入到viewcontent中 public void setcontentview(view view) { mviewcontent.addview(view); } // 设置滑动回调 public void setonslidelistener(onslidelistener onslidelistener) { monslidelistener = onslidelistener; } // 将当前状态置为关闭 public void shrink() { if (getscrollx() != 0) { this.smoothscrollto(0, 0); } } // 根据motionevent来进行滑动,这个方法的作用相当于ontouchevent // 如果你不需要处理滑动冲突,可以直接重命名,照样能正常工作 public void onrequiretouchevent(motionevent event) { int x = (int) event.getx(); int y = (int) event.gety(); int scrollx = getscrollx(); log.d(tag, "x=" + x + " y=" + y); switch (event.getaction()) { case motionevent.action_down: { if (!mscroller.isfinished()) { mscroller.abortanimation(); } if (monslidelistener != null) { monslidelistener.onslide(this, onslidelistener.slide_status_start_scroll); } break; } case motionevent.action_move: { int deltax = x - mlastx; int deltay = y - mlasty; if (math.abs(deltax) < math.abs(deltay) * tan) { // 滑动不满足条件,不做横向滑动 break; } // 计算滑动终点是否合法,防止滑动越界 int newscrollx = scrollx - deltax; if (deltax != 0) { if (newscrollx < 0) { newscrollx = 0; } else if (newscrollx > mholderwidth) { newscrollx = mholderwidth; } this.scrollto(newscrollx, 0); } break; } case motionevent.action_up: { int newscrollx = 0; // 这里做了下判断,当松开手的时候,会自动向两边滑动,具体向哪边滑,要看当前所处的位置 if (scrollx - mholderwidth * 0.75 > 0) { newscrollx = mholderwidth; } // 慢慢滑向终点 this.smoothscrollto(newscrollx, 0); // 通知上层滑动事件 if (monslidelistener != null) { monslidelistener.onslide(this, newscrollx == 0 ? onslidelistener.slide_status_off : onslidelistener.slide_status_on); } break; } default: break; } mlastx = x; mlasty = y; } private void smoothscrollto(int destx, int desty) { // 缓慢滚动到指定位置 int scrollx = getscrollx(); int delta = destx - scrollx; // 以三倍时长滑向destx,效果就是慢慢滑动 mscroller.startscroll(scrollx, 0, delta, 0, math.abs(delta) * 3); invalidate(); } @override public void computescroll() { if (mscroller.computescrolloffset()) { scrollto(mscroller.getcurrx(), mscroller.getcurry()); postinvalidate(); } } }
自定义slideview所关联的xml.(主要就是布局侧滑显出来的删除按钮)
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <linearlayout android:id="@+id/view_content" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > </linearlayout> <relativelayout android:id="@+id/holder" android:layout_width="120dp" android:layout_height="match_parent" android:clickable="true" android:background="@drawable/holder_bg"> <textview android:id="@+id/delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableleft="@drawable/del_icon_normal" android:layout_centerinparent="true" android:gravity="center" android:textcolor="@color/floralwhite" android:text="删除" /> </relativelayout> </merge>
自定义listviewcompat继承listview。
import com.ryg.slideview.mainactivity.messageitem; import android.content.context; import android.util.attributeset; import android.util.log; import android.view.motionevent; import android.view.view; import android.widget.listview; public class listviewcompat extends listview { private static final string tag = "listviewcompat"; private slideview mfocuseditemview; public listviewcompat(context context) { super(context); } public listviewcompat(context context, attributeset attrs) { super(context, attrs); } public listviewcompat(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); } public void shrinklistitem(int position) { view item = getchildat(position); if (item != null) { try { ((slideview) item).shrink(); } catch (classcastexception e) { e.printstacktrace(); } } } @override public boolean ontouchevent(motionevent event) { switch (event.getaction()) { case motionevent.action_down: { int x = (int) event.getx(); int y = (int) event.gety(); //根据坐标获取item所在的行 int position = pointtoposition(x, y); log.e(tag, "postion=" + position); if (position != invalid_position) { //得到当前点击行的数据从而取出当前行的item。 //可能有人怀疑,为什么要这么干?为什么不用getchildat(position)? //因为listview会进行缓存,如果你不这么干,有些行的view你是得不到的。 messageitem data = (messageitem) getitematposition(position); mfocuseditemview = data.slideview; log.e(tag, "focuseditemview=" + mfocuseditemview); } } default: break; } //向当前点击的view发送滑动事件请求,其实就是向slideview发请求 if (mfocuseditemview != null) { mfocuseditemview.onrequiretouchevent(event); } return super.ontouchevent(event); } }
接下来就是主界面的activity了
import java.util.arraylist; import java.util.list; import com.ryg.slideview.slideview.onslidelistener; import android.app.activity; import android.os.bundle; import android.util.log; import android.view.layoutinflater; import android.view.view; import android.view.view.onclicklistener; import android.view.viewgroup; import android.widget.adapterview; import android.widget.adapterview.onitemclicklistener; import android.widget.baseadapter; import android.widget.imageview; import android.widget.listview; import android.widget.textview; public class mainactivity extends activity implements onitemclicklistener, onclicklistener, onslidelistener { private static final string tag = "mainactivity"; private listviewcompat mlistview; private list<messageitem> mmessageitems = new arraylist<mainactivity.messageitem>(); private slideadapter mslideadapter; // 上次处于打开状态的slideview private slideview mlastslideviewwithstatuson; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); } private void initview() { mlistview = (listviewcompat) findviewbyid(r.id.list); for (int i = 0; i < 20; i++) { messageitem item = new messageitem(); if (i % 2 == 0) { item.iconres = r.drawable.default_qq_avatar; item.title = "腾讯新闻"; item.msg = "天津大爆炸:河北大爆炸"; item.time = "晚上18:00"; } else { item.iconres = r.drawable.wechat_icon; item.title = "微信团队"; item.msg = "欢迎你使用微信"; item.time = "10月01日"; } mmessageitems.add(item); } mslideadapter = new slideadapter(); mlistview.setadapter(mslideadapter); mlistview.setonitemclicklistener(this); } //listview 的适配器 private class slideadapter extends baseadapter { private layoutinflater minflater; slideadapter() { super(); minflater = getlayoutinflater(); } @override public int getcount() { return mmessageitems.size(); } @override public object getitem(int position) { return mmessageitems.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { viewholder holder; slideview slideview = (slideview) convertview; if (slideview == null) { // 这里是我们的item view itemview = minflater.inflate(r.layout.list_item, null); slideview = new slideview(mainactivity.this); // 这里把item加入到slideview slideview.setcontentview(itemview); // 下面是做一些数据缓存 holder = new viewholder(slideview); slideview.setonslidelistener(mainactivity.this); slideview.settag(holder); } else { holder = (viewholder) slideview.gettag(); } messageitem item = mmessageitems.get(position); item.slideview = slideview; item.slideview.shrink(); holder.icon.setimageresource(item.iconres); holder.title.settext(item.title); holder.msg.settext(item.msg); holder.time.settext(item.time); holder.deleteholder.setonclicklistener(mainactivity.this); return slideview; } } public class messageitem { public int iconres; public string title; public string msg; public string time; public slideview slideview; } private static class viewholder { public imageview icon; public textview title; public textview msg; public textview time; public viewgroup deleteholder; viewholder(view view) { icon = (imageview) view.findviewbyid(r.id.icon); title = (textview) view.findviewbyid(r.id.title); msg = (textview) view.findviewbyid(r.id.msg); time = (textview) view.findviewbyid(r.id.time); deleteholder = (viewgroup) view.findviewbyid(r.id.holder); } } @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { // 这里处理listitem的点击事件 log.e(tag, "onitemclick position=" + position); } @override public void onslide(view view, int status) { // 如果当前存在已经打开的slideview,那么将其关闭 if (mlastslideviewwithstatuson != null && mlastslideviewwithstatuson != view) { mlastslideviewwithstatuson.shrink(); } // 记录本次处于打开状态的view if (status == slide_status_on) { mlastslideviewwithstatuson = (slideview) view; } } @override public void onclick(view v) { // 这里处理删除按钮的点击事件,可以删除对话 if (v.getid() == r.id.holder) { int position = mlistview.getpositionforview(v); if (position != listview.invalid_position) { mmessageitems.remove(position); mslideadapter.notifydatasetchanged(); } log.e(tag, "onclick v=" + v); } }
主界面中activity用到的xml
<linearlayout 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.ryg.slideview.listviewcompat android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#fff4f7f9" android:cachecolorhint="#00000000" android:divider="#dddbdb" android:dividerheight="1.0px" android:drawselectorontop="false" android:listselector="@android:color/transparent" android:scrollbars="none" /> </linearlayout>
listview适配器的布局。
<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="58dp" android:background="@drawable/list_item_bg" android:descendantfocusability="blocksdescendants" android:gravity="center_vertical" android:paddingbottom="5dp" android:paddingleft="10dp" android:paddingright="10dp" android:paddingtop="5dp" > <imageview android:id="@+id/icon" android:layout_width="50dp" android:layout_height="50dp" android:layout_marginright="5dp" android:src="@drawable/wechat_icon" /> <textview android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_torightof="@id/icon" android:textcolor="@color/black" android:textsize="15sp" /> <textview android:id="@+id/msg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignbottom="@id/icon" android:layout_alignleft="@id/title" android:textcolor="@color/grey" /> <textview android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignparentright="true" android:layout_aligntop="@id/title" android:textcolor="@color/grey" /> </relativelayout>
附drawable目录下的xml文件
holder_bg.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/dimgrey" android:state_pressed="true"></item> <item android:drawable="@color/dimgrey" android:state_focused="true"></item> <item android:drawable="@color/dimgrey" android:state_selected="true"></item> <item android:drawable="@color/gray" android:state_enabled="true"></item> <item android:drawable="@color/gray" android:state_enabled="false"></item> </selector>
list_item_bg.xml.
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/item_point_bg" android:state_pressed="true"></item> <item android:drawable="@drawable/item_point_bg" android:state_focused="true"></item> <item android:drawable="@drawable/item_point_bg" android:state_selected="true"></item> <item android:drawable="@android:color/transparent" android:state_enabled="true"></item> <item android:drawable="@android:color/transparent" android:state_enabled="false"></item> </selector>
还有一些对color颜色的定义,这里就不贴了,大家可以直接换成颜色的代码就可以。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。