尊重原创,转载请注明(http://blog.csdn.net/aoshiwenrou/article/details/42971193)

最近的项目需求有一条是要实现仿QQ的侧滑删除效果,网上搜到了很多,但是与预想的都不太一样,于是自己研究了一下,写了一个Demo,记录下来。

功能:

1.实现了仿QQ的Item侧滑效果

2.可根据item的长度计算侧滑范围

3.实现item条目点击监听与删除按钮监听

4.解决了删除按钮出现时ListView滑动错位问题(偷懒:滑动时item复位)

主要代码:

SlidePauseListView:

package com.jcking.slidepauselistview.view;import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Scroller;public class SlidePauseListView extends ListView {/*** 当前滑动的ListView position*/private int slidePosition;/*** 手指按下X的坐标*/private int downY;/*** 手指按下Y的坐标*/private int downX;/*** 屏幕宽度*/private int screenWidth;/*** ListView的item*/private View itemView;/*** 滑动类*/private Scroller scroller;/*** 滑动速度边界值*/private static final int SNAP_VELOCITY = 600;/*** 速度追踪对象*/private VelocityTracker velocityTracker;/*** 是否响应滑动,默认为不响应*/private boolean isSlide = false;/*** 认为是用户滑动的最小距离*/private int mTouchSlop;/*** 最多可滑动的范围 */private int mTouchLimit;/*** 是否已经侧滑*/private boolean hasSlided = false;public SlidePauseListView(Context context) {this(context, null);}public SlidePauseListView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SlidePauseListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);DisplayMetrics dm = new DisplayMetrics();((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(dm);screenWidth = dm.widthPixels;scroller = new Scroller(context);mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();mTouchLimit = screenWidth / 4;}/*** 设置可滑动的最大值* @param limit*/public void setTouchLimit(int limit){this.mTouchLimit = limit;}/*** 获取可滑动的最大值* @return*/public int getTouchLimit(){return this.mTouchLimit;}/*** 退回原位*/public void slideBack(){if(hasSlided && itemView != null){scrollXBy(itemView, -mTouchLimit);hasSlided = false;}}/*** 分发事件,主要做的是判断点击的是那个item, 以及通过postDelayed来设置响应左右滑动事件*/@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {addVelocityTracker(event);// 假如scroller滚动还没有结束,我们直接返回if (!scroller.isFinished()) {return super.dispatchTouchEvent(event);}// 如果已经侧滑了,我们直接返回if(hasSlided){return super.dispatchTouchEvent(event);}downX = (int) event.getX();downY = (int) event.getY();slidePosition = pointToPosition(downX, downY);// 无效的position, 不做任何处理if (slidePosition == AdapterView.INVALID_POSITION) {return super.dispatchTouchEvent(event);}// 获取我们点击的item viewitemView = getChildAt(slidePosition - getFirstVisiblePosition());if(itemView instanceof SlideListItem){SlideListItem item = (SlideListItem) itemView;int count = item.getChildCount();mTouchLimit = 0;for (int i = 0; i < count; i++) {if(i > 0){mTouchLimit += item.getChildAt(i).getMeasuredWidth();}}}break;}case MotionEvent.ACTION_MOVE: {
//          Log.d("test", "downX : " + downX + ", event.getX() : " + event.getX() + ", mTouchSlop : " + mTouchSlop);if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY|| (downX - event.getX() > mTouchSlop && Math.abs(event.getY() - downY) < mTouchSlop)) {isSlide = true;}break;}case MotionEvent.ACTION_UP:recycleVelocityTracker();break;}return super.dispatchTouchEvent(event);}/*** 处理我们拖动ListView item的逻辑*/@Overridepublic boolean onTouchEvent(MotionEvent ev) {Log.d("test", "hasSlided : " + hasSlided + ", isSlide : " + isSlide);// 如果已经侧滑了,回归原位if(hasSlided){scrollXBy(itemView, -mTouchLimit);hasSlided = false;// 拦截事件,不继续处理return false;}if (isSlide && slidePosition != AdapterView.INVALID_POSITION) {requestDisallowInterceptTouchEvent(true);addVelocityTracker(ev);final int action = ev.getAction();int x = (int) ev.getX();switch (action) {case MotionEvent.ACTION_DOWN:break;case MotionEvent.ACTION_MOVE:MotionEvent cancelEvent = MotionEvent.obtain(ev);cancelEvent.setAction(MotionEvent.ACTION_CANCEL |(ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));onTouchEvent(cancelEvent);int deltaX = downX - x;downX = x;// 手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚scrollXBy(itemView, deltaX);return true;  //拖动的时候ListView不滚动case MotionEvent.ACTION_UP:int velocityX = getScrollVelocity();if (velocityX < -SNAP_VELOCITY) {scrollLeft();} else {scrollByDistanceX();}recycleVelocityTracker();// 手指离开的时候就不响应左右滚动isSlide = false;break;}}//        boolean result = super.onTouchEvent(ev);
//      Log.d("test", "result : " + result);//否则直接交给ListView来处理onTouchEvent事件return super.onTouchEvent(ev);}/*** 让指定的view滚动x位置,设置左右边界,view最多滚动到边界位置* @param view* @param x*/private void scrollXBy(View view, int x){// 如果已经滑动了最大值,并希望继续向左滑,忽略if(view.getScrollX() >= mTouchLimit && x >= 0) return;// 如果已经回到原位,并希望继续想右划,忽略if(view.getScrollX() <= 0 && x <= 0) return;if(view.getScrollX() + x > mTouchLimit){x = mTouchLimit - view.getScrollX();}else if(view.getScrollX() + x < 0){x = -view.getScrollX();}view.scrollBy(x, 0);}@Overridepublic void computeScroll() {// 调用startScroll的时候scroller.computeScrollOffset()返回true,if (scroller.computeScrollOffset()) {// 让ListView item根据当前的滚动偏移量进行滚动itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());postInvalidate();}}/*** 向左滑动,根据上面我们知道向左滑动为正值*/private void scrollLeft() {final int delta = (mTouchLimit - itemView.getScrollX());// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动itemscroller.startScroll(itemView.getScrollX(), 0, delta, 0,Math.abs(delta));postInvalidate(); // 刷新itemViewhasSlided = true;}/*** 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动*/private void scrollByDistanceX() {// 如果向左滚动的距离大于屏幕的二分之一,就让其删除if (itemView.getScrollX() >= mTouchLimit / 2) {scrollLeft();} else {// 滚回到原始位置,为了偷下懒这里是直接调用scrollTo滚动itemView.scrollTo(0, 0);}}/*** 添加用户的速度跟踪器* * @param event*/private void addVelocityTracker(MotionEvent event) {if (velocityTracker == null) {velocityTracker = VelocityTracker.obtain();}velocityTracker.addMovement(event);}/*** 移除用户速度跟踪器*/private void recycleVelocityTracker() {if (velocityTracker != null) {velocityTracker.recycle();velocityTracker = null;}}/*** 获取X方向的滑动速度,大于0向右滑动,反之向左* * @return*/private int getScrollVelocity() {velocityTracker.computeCurrentVelocity(1000);int velocity = (int) velocityTracker.getXVelocity();return velocity;}}

继承ListView,实现主要功能, dispatchTouchEvent进行事件分发,由自身的 onTouchEvent方法处理侧滑事件。其中

if(itemView instanceof SlideListItem){SlideListItem item = (SlideListItem) itemView;int count = item.getChildCount();mTouchLimit = 0;for (int i = 0; i < count; i++) {if(i > 0){mTouchLimit += item.getChildAt(i).getMeasuredWidth();}}}

通过计算item的长度,设定侧滑的最大偏移量。ScrollXBy方法,防止侧滑越界。

/*** 让指定的view滚动x位置,设置左右边界,view最多滚动到边界位置* @param view* @param x*/private void scrollXBy(View view, int x){// 如果已经滑动了最大值,并希望继续向左滑,忽略if(view.getScrollX() >= mTouchLimit && x >= 0) return;// 如果已经回到原位,并希望继续想右划,忽略if(view.getScrollX() <= 0 && x <= 0) return;if(view.getScrollX() + x > mTouchLimit){x = mTouchLimit - view.getScrollX();}else if(view.getScrollX() + x < 0){x = -view.getScrollX();}view.scrollBy(x, 0);}

SlideListItem:继承LinearLayout,主要通过重写onLayout方法使其中的子View不压缩变形。

package com.jcking.slidepauselistview.view;import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;public class SlideListItem extends LinearLayout {public SlideListItem(Context context) {this(context, null);}public SlideListItem(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int count = getChildCount();for (int i = 0; i < count; i++) {measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);}}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.d("test", "event : " + event);return super.dispatchTouchEvent(event);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int margeLeft = 0;int size = getChildCount();for (int i = 0; i < size; i++) {View view = getChildAt(i);if (view.getVisibility() != View.GONE) {int childWidth = view.getMeasuredWidth();// 将内部子孩子横排排列view.layout(margeLeft, 0, margeLeft + childWidth,view.getMeasuredHeight());margeLeft += childWidth;}}}
}

SlideAdapter:在Adapter中绑定Click事件,分别监听。

package com.jcking.slidepauselistview;import java.util.List;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.TextView;public class SlideAdapter extends BaseAdapter {private List<String> mData;private LayoutInflater mInflater;private ViewHolder mHolder;private OnSlideClickListener mListener;class ViewHolder{TextView tv;View front;Button btnClock;Button btnDelete;}public SlideAdapter(Context context, List<String> data, OnSlideClickListener listener){this.mData = data;this.mInflater = LayoutInflater.from(context);this.mListener = listener;}@Overridepublic int getCount() {return mData == null ? 0 : mData.size();}@Overridepublic String getItem(int position) {return mData == null ? null : mData.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if(convertView == null){convertView = mInflater.inflate(R.layout.item_listview, null);mHolder = new ViewHolder();mHolder.tv = (TextView) convertView.findViewById(R.id.tv);mHolder.front = convertView.findViewById(R.id.front);mHolder.btnClock = (Button) convertView.findViewById(R.id.btnClock);mHolder.btnDelete = (Button) convertView.findViewById(R.id.btnDelete);convertView.setTag(mHolder);}else{mHolder = (ViewHolder) convertView.getTag();}mHolder.tv.setText(getItem(position));// 使隐藏的按钮数量不一样,测试可滑动范围mHolder.btnClock.setVisibility(position%2==0 ? View.GONE : View.VISIBLE);if(mListener != null){final int pos = position;final View item = convertView;mHolder.btnClock.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.onClockClick(pos, item);}});mHolder.btnDelete.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.onDeleteClick(pos, item);}});mHolder.front.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.onItemClick(pos, item);}});}return convertView;}public interface OnSlideClickListener{public void onItemClick(int position, View item);public void onClockClick(int position, View item);public void onDeleteClick(int position, View item);}
}

MainActivity:简单调用

package com.jcking.slidepauselistview;import java.util.ArrayList;
import java.util.List;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;import com.jcking.slidepauselistview.SlideAdapter.OnSlideClickListener;
import com.jcking.slidepauselistview.view.SlidePauseListView;public class MainActivity extends Activity implements OnSlideClickListener {private SlidePauseListView slideCutListView;private SlideAdapter adapter;private List<String> dataSourceList = new ArrayList<String>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}private void init() {slideCutListView = (SlidePauseListView) findViewById(R.id.slideCutListView);for (int i = 0; i < 20; i++) {dataSourceList.add("滑动删除" + i);}adapter = new SlideAdapter(this, dataSourceList, this);slideCutListView.setAdapter(adapter);}@Overridepublic void onItemClick(int position, View item) {// TODO Auto-generated method stubToast.makeText(this, "点击条目  " + position, Toast.LENGTH_SHORT).show();}@Overridepublic void onClockClick(int position, View item) {// TODO Auto-generated method stubToast.makeText(this, "点击闹钟  " + position, Toast.LENGTH_SHORT) .show();}@Overridepublic void onDeleteClick(int position, View item) {// TODO Auto-generated method stubToast.makeText(this, "点击删除  " + position, Toast.LENGTH_SHORT) .show();slideCutListView.slideBack();dataSourceList.remove(position);adapter.notifyDataSetChanged();}}

activity_main:主视图布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><com.jcking.slidepauselistview.view.SlidePauseListViewandroid:id="@+id/slideCutListView"android:layout_width="match_parent"android:layout_height="match_parent"android:cacheColorHint="@android:color/transparent"android:divider="#2b2b2b"android:dividerHeight="0.5dp"android:listSelector="@android:color/transparent" ></com.jcking.slidepauselistview.view.SlidePauseListView></LinearLayout>

item_listview:item布局

<?xml version="1.0" encoding="UTF-8"?>
<com.jcking.slidepauselistview.view.SlideListItem xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#acacac"android:orientation="horizontal" ><!-- 这里一定要设置android:layout_width属性为充满屏幕  --><LinearLayoutandroid:id="@+id/front"android:layout_width="match_parent"android:layout_height="wrap_content"android:clickable="true"android:gravity="center_vertical"android:orientation="horizontal"android:padding="5dp" ><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="测试数据"android:textSize="18sp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal" ><Buttonandroid:id="@+id/btnClock"android:layout_width="wrap_content"android:layout_height="match_parent"android:background="#ff0000"android:text="闹铃"android:textColor="#ffffff" /><Buttonandroid:id="@+id/btnDelete"android:layout_width="wrap_content"android:layout_height="match_parent"android:background="#00ffff"android:text="删除"android:textColor="#ffffff" /></LinearLayout></com.jcking.slidepauselistview.view.SlideListItem>

提示:

1.item布局中,第一个一定要充满屏幕

问题:

1.灵敏度不高,不能像QQ一样完全精确识别手势

参考:http://blog.csdn.net/xiaanming/article/details/17539199

http://blog.csdn.net/jwzhangjie/article/details/39006007

点击此处,下载源码

Android ListView侧滑item,仿QQ删除效果相关推荐

  1. Android开发之高仿QQ消息侧拉删除

    Android开发之高仿QQ消息侧拉删除 QQ消息的侧滑删除效果之炫酷,想必大家都见过吧,本人作为一名安卓开发人员,遇到如此炫酷的效果,怎能不研究一番呢,现本人已实现其基本功能,现将代码贴出,望各位大 ...

  2. android listview中item通过viewpager实现

    android listview中item通过viewpager实现 android listview中item通过viewpager实现,每一个item都支持viewpager实现图片切换功能.本项 ...

  3. iOS仿QQ分组效果

    本篇主要讲解仿QQ分组效果的实现,通过本遍的学习,估计都可以自己去实现了(老司机可以),在这里只说仿QQ分组的效果,代码简单,逻辑清晰.其他的功能的可以自行添加,好了,进入主题吧. 效果图 下面的是其 ...

  4. android自定义设置界面,Android开发之精仿QQ设置界面(自定义PreferenceActivity)

    Android开发之精仿QQ设置界面(自定义PreferenceActivity) 时间:2011-12-05 10:25:06 来源:Android开发者门户 作者: 今天,再给大家分享一下QQ设置 ...

  5. 名片夹android布局代码,Android自定义布局实现仿qq侧滑部分代码

    自定义布局实现仿qq侧滑部分Android代码,供大家参考,具体内容如下 实现说明: 通过自定义布局实现: SlidingLayout继承于 HorizontalScrollView /** * Cr ...

  6. android仿qq布局,Android自定义布局实现仿qq侧滑部分代码

    自定义布局实现仿qq侧滑部分android代码,供大家参考,具体内容如下 实现说明: 通过自定义布局实现: slidinglayout继承于 horizontalscrollview /** * cr ...

  7. Android自定义View之仿QQ运动步数进度效果

    文章目录 前言 先看效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6e4ddec17933496ea4830fa08d8ffbe5.png?x-oss-pr ...

  8. android 自定义view实现仿QQ运动步数进度效果

    最近公司在策划一个新的项目,原型还没出来,再说这公司人都要走没了,估计又要找工作了,所以必须要学习,争取每个写个关于自定义view方面的,这样几个月积累下来,也能学习到东西,今天就带来简单的效果,就是 ...

  9. android气泡聊天消息背景,Android使用贝塞尔曲线仿QQ聊天消息气泡拖拽效果

    本文实例为大家分享了Android仿QQ聊天消息气泡拖拽效果展示的具体代码,供大家参考,具体内容如下 先画圆,都会吧.代码如下: public class Bezier extends View { ...

最新文章

  1. 使用autoconf完成编译配置
  2. R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(自定义调色板填充色、dark2、灰度比例)实战
  3. spring MVC RequestMappingHandlerMapping解析
  4. DataWorks百问百答01:数据同步该用什么资源组
  5. 初始化请求例子_当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?
  6. php 修改文件的权限_php修改文件权限
  7. Excel VBA 统分
  8. 【https】 1 HTTP Security (bb102-1)
  9. 基因表达式编程(GEP)自学 第【7】天 Python 实现
  10. wap pc html,PCWAP手机PC网站信息管理系统 v1.4.3
  11. php 取出最后一数组元素,PHP取出数组中最后一个元素的方法汇总
  12. VS2010中使用AnkhSvn
  13. 使用 SendARP 获取 MAC 地址
  14. 国产电子书的代表作是什么样的?
  15. antV报错ResizeObserver loop limit exceeded
  16. 都市青年图鉴:那些喊着奋斗的人,后来怎样了
  17. 作为一名Java开发工程师需要掌握哪些专业技能
  18. 论文笔记 -- Fast-LIO -- ESIKF溯源
  19. 超快激光微加工的Burst Mode和PSO功能
  20. kali密码破解工具

热门文章

  1. element显示true或者false_element-ui轮播的简单实现
  2. android edittext seterror,EditText之setError方法一二
  3. Spring Boot WebMagic 入库时 mapper注入提示空指针,以及正确的操作
  4. VC++动态链接库(DLL)编程深入浅出
  5. 每天一道LeetCode-----将字符串拆分成有效的ip地址
  6. 控制台怎么退出mysql_退出mysql控制台与退出mysql
  7. tomcat 4.0头文件及maven导出问题万能build模板
  8. LVS(11)——wrr
  9. php rpc连接Python,PHP语言之Python、PHP通过xml-rpc进行通信,xml-rpc中文的解决
  10. 电脑重装系统后提示invalid partition table怎么解决