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

android实现QQ微信侧滑删除效果

程序员文章站 2022-08-03 18:19:13
最近由于项目需求,需要做一个listview中的item策划删除的效果,与是查找资料和参考了一些相关的博客,终于完美实现了策划删除的效果。 先看一下效果图(研究了半天...

最近由于项目需求,需要做一个listview中的item策划删除的效果,与是查找资料和参考了一些相关的博客,终于完美实现了策划删除的效果。

先看一下效果图(研究了半天竟然没研究出来真机上gif图怎么做,大家将就看一下吧)。

android实现QQ微信侧滑删除效果     

侧滑效果图

android实现QQ微信侧滑删除效果

点击删除后的截图

android实现QQ微信侧滑删除效果

点击删除后,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颜色的定义,这里就不贴了,大家可以直接换成颜色的代码就可以。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。