这个效果的完成主要分为两个部分

  1. 自定义view作为listview的列表项 一个view里面包括 显示头像,名字,消息内容等的contentView和滑动才能显示出来的删除,置顶的右边菜单menuView 在手指移动的时候同时改变这两个视图的位置

  2. 重写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上的实现此效果的开源项目

Scroller的使用

模仿qq列表信息滑动删除效果的更多相关文章

  1. android中列表的滑动删除仿ios滑动删除

    大家是不是觉得ios列表的滑动删除效果很酷炫?不用羡慕android也可以实现相同的效果 并且可以自定义效果,比如左滑删除,置顶,收藏,分享等等 其实就是自定义listview重写listview方法 ...

  2. 【转】Android 实现ListView的滑动删除效果

    http://www.cnblogs.com/weixiao870428/p/3524055.html http://download.csdn.net/download/love_javc_you/ ...

  3. Android-自定义仿QQ列表Item滑动

    效果图: 布局中去指定自定义FrameLayout: <!-- 自定义仿QQ列表Item滑动 --> <view.custom.shangguigucustomview.MyCust ...

  4. android QQ消息左滑动删除实例(优化版SwipeListViewEX)

    仿 QQ消息左滑动删除item消息实例 源代码参考:http://blog.csdn.net/gaolei1201/article/details/42677951 自己作了一些调整,全部代码下载地址 ...

  5. 原生H5页面模拟APP左侧滑动删除效果

    话不多说,往左侧滑动,显示删除,我们先来看一下效果图:如下: 这个布局我就不多说,反正就是一行ul,li, class名“item” js代码如下: $(".item").on(& ...

  6. 仿QQ列表左滑删除

    一直想写个仿QQ通讯列表左滑删除的效果,今天终于忙里偷闲,简单一个. 大概思路是这样的: 通过 ontouchstartontouchmoveontouchend 结合css3的平移. 不多说,直接上 ...

  7. ListView滑动删除效果实现

    通过继承ListView然后结合PopupWindow实现 首先是布局文件: delete_btn.xml:这里只需要一个Button <?xml version="1.0" ...

  8. android中listview的item滑动删除效果(已解决listview点击问题)

    领导看到iphone上tableview有个滑动删除的效果,要求在android上也实现,搜了下资料,实现起来比较简单,可弄到后面,居然不能点击了,把一篇文章中的代码修改了一下,捣鼓了一番,搞定,下面 ...

  9. wing带你玩转自定义view系列(2) 简单模仿qq未读消息去除效果

    上一篇介绍了贝塞尔曲线的简单应用 仿360内存清理效果 这一篇带来一个  两条贝塞尔曲线的应用 : 仿qq未读消息去除效果. 转载请注明出处:http://blog.csdn.net/wingicho ...

随机推荐

  1. Bulk Rename Utility 3.0 + x64 中文汉化版

    Bulk Rename Utility 3.0 + x64 中文汉化版由大眼仔旭(www.dayanzai.me)汉化发布.当发现做一件事情,原本用工具或软件进行批量处理也能达到相同效果,可却花了数倍 ...

  2. nginx 源码安装以及后续升级https

    事情的来源是,公司要将网站从http升级到https,由于历史遗留原因,才发现现有的nginx是通过源码安装的,并没有安装ssl模块,需要现安装sll模块,这个nginx是整个公司最前端的一个代理,涉 ...

  3. MongoDB安装及启动

    本机环境系统:Debian 9桌面系统:KDE Plasma ## 官网下载自己系统最新稳定版 https://www.mongodb.com/download-center#community 选择 ...

  4. Quick find Helper

    using System; using Microsoft.Xrm.Sdk; using Microsoft.Crm.Sdk.Messages; /// <summary> /// 视图 ...

  5. 触摸屏之linux3.4.2安装tslib

    1. 写好触摸屏驱动后,安装tslib 1.1 tar xzf tslib-1.4.tar.gz 1.2 cd tslib 1.3 修改编译器版本号或者内核版本号,使它们一致.不然会出错,显示sele ...

  6. jenkins+maven+docker集成java发布(二)#远程发布

    jenkins+maven+docker集成java发布(一)中写了在Jenkins服务器自动部署业务,那需要将java项目部署到其他服务器怎么操作 这里需要依赖插件Publish Over SSH ...

  7. 20155220 2016-2017-2 《java程序设计》第四周总结

    教材学习内容总结 第六章 继承与多态 继承 继承的基本原则是: 子类继承父类的所有成员变量(包括静态成员): 子类继承除父类构造方法外的所有成员方法(包括静态方法): 子类不能继承父类的构造方法,但在 ...

  8. 20155235 2016-2017-2《Java程序设计》课程总结

    每周作业链接汇总 预备作业一:学期前作业 预备作业二:技能获取与C语言学习情况 预备作业三:安装虚拟机与Linux的学习 第一周作业:20155235 2006-2007-2 <Java程序设计 ...

  9. 20155306 2016-2017-2 《Java程序设计》第十周学习总结

    20155306 2016-2017-2 <Java程序设计>第十周学习总结 教材学习内容总结 Java和Android开发学习(网络) 网络概览 计算机网络体系结构的通信协议划分为七层, ...

  10. 【SDOI2014】数表

    题面 题解 不管$a$的限制 我们要求的东西是:($\sigma(x)$是$x$的约数个数和) $ \sum_{i=1}^n\sum_{j=1}^m\sigma(gcd(i,j)) $ 设$f(x)= ...