Android开发之自定义侧滑ListView
来源:互联网 发布:宿迁网络问政政策 编辑:程序博客网 时间:2024/06/03 16:42
不知道是上哪个版本的知乎来着,可以实现用手指侧滑取消关注话题什么的,后来改版了,这个效果又没了。当时我是相当喜欢这个功能的,不想看什么鬼玩意,手指一滑就没了,挺方便的。后来想了很久,再看了下别人的代码,实现这个功能还是不难的。
首先分析一下这个功能该怎么实现,很容易想到是去写一个类去继承ListView,之所以不去继承BaseAdapter去实现这个功能,是因为有很多涉及到界面的操作,BaseAdapter里面没有相关的API。
确定了是去继承ListView,接下来怎么样呢?必不可少的去重写onTouchEvent监听手指触摸ListView的操作,获得手指在ListView上触摸的X,Y轴的坐标,然后通过int pointToPosition(int x,int y)去获取当前触碰到的是哪一个item,然后通过View getChildAt(int position)去获得当前操作的item,然后通过ViewHelper去改变item的状态(比如透明度和X坐标)。当手指抬起的时候,获取手指的位置,如果此时位置没有超过我们觉得用户想移除item的范围,可以理解为用户不想将这个item移除,用属性动画把item移回来;如果超过了我们觉得用户想移除item的范围,用属性动画把item移出去,把其余所有在原本item下面的item上移一位,各位注意了,此时这些操作全部都是视觉上的,也就是本身不会对ListView真正的操作,所以此时还需要定义一个接口,当视觉上的操作结束之后,回调接口,去执行真正的对于ListView内容上的变动。
@Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: handleActionDown(ev); break; case MotionEvent.ACTION_MOVE: return handleActionMove(ev); case MotionEvent.ACTION_UP: handleActionUp(ev); break; } return super.onTouchEvent(ev); } /** * 按下事件处理 * * @param ev * @return */ private void handleActionDown(MotionEvent ev) { mDownX = ev.getX(); mDownY = ev.getY(); mDownPosition = pointToPosition((int) mDownX, (int) mDownY); if (mDownPosition == AdapterView.INVALID_POSITION) { return; } mDownView = getChildAt(mDownPosition - getFirstVisiblePosition()); if (mDownView != null) { mViewWidth = mDownView.getWidth(); } //加入速度检测 mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); } /** * 处理手指滑动的方法 * * @param ev * @return */ private boolean handleActionMove(MotionEvent ev) { if (mVelocityTracker == null || mDownView == null) { return super.onTouchEvent(ev); } // 获取X方向滑动的距离 float deltaX = ev.getX() - mDownX; float deltaY = ev.getY() - mDownY; // X方向滑动的距离大于mSlop并且Y方向滑动的距离小于mSlop,表示可以滑动 if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop) { mSwiping = true; //当手指滑动item,取消item的点击事件,不然我们滑动Item也伴随着item点击事件的发生 MotionEvent cancelEvent = MotionEvent.obtain(ev); cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); onTouchEvent(cancelEvent); } if (mSwiping) { // 跟谁手指移动item ViewHelper.setTranslationX(mDownView, deltaX); // 透明度渐变 ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth))); // 手指滑动的时候,返回true,表示SwipeDismissListView自己处理onTouchEvent,其他的就交给父类来处理 return true; } return super.onTouchEvent(ev); } /** * 手指抬起的事件处理 * * @param ev */ private void handleActionUp(MotionEvent ev) { if (mVelocityTracker == null || mDownView == null || !mSwiping) { return; } float deltaX = ev.getX() - mDownX; //通过滑动的距离计算出X,Y方向的速度 mVelocityTracker.computeCurrentVelocity(1000); float velocityX = Math.abs(mVelocityTracker.getXVelocity()); float velocityY = Math.abs(mVelocityTracker.getYVelocity()); boolean dismiss = false; //item是否要滑出屏幕 boolean dismissRight = false;//是否往右边删除 //当拖动item的距离大于item的一半,item滑出屏幕 if (Math.abs(deltaX) > mViewWidth / 2) { dismiss = true; dismissRight = deltaX > 0; //手指在屏幕滑动的速度在某个范围内,也使得item滑出屏幕 } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX) { dismiss = true; dismissRight = mVelocityTracker.getXVelocity() > 0; } if (dismiss) { AnimatorSet set = new AnimatorSet(); set.playTogether(ObjectAnimator.ofFloat(mDownView, "translationX", dismissRight ? mViewWidth : -mViewWidth), ObjectAnimator.ofFloat(mDownView, "alpha", 0)); set.setDuration(mAnimationTime).start(); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //Item滑出界面之后执行删除 performDismiss(mDownView, mDownPosition); } }); } else { //将item滑动至开始位置 com.nineoldandroids.view.ViewPropertyAnimator.animate(mDownView) .translationX(0) .alpha(1) .setDuration(mAnimationTime).setListener(null); } //移除速度检测 if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } mSwiping = false; } /** * 在此方法中执行item删除之后,其他的item向上或者向下滚动的动画,并且将position回调到方法onDismiss()中 * * @param dismissView * @param dismissPosition */ private void performDismiss(final View dismissView, final int dismissPosition) { final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();//获取item的布局参数 final int originalHeight = dismissView.getHeight();//item的高度 ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 0).setDuration(mAnimationTime); animator.start(); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (onDismissCallback != null) { onDismissCallback.onDismiss(dismissPosition); } //这段代码很重要,因为我们并没有将item从ListView中移除,而是将item的高度设置为0 //所以我们在动画执行完毕之后将item设置回来 ViewHelper.setAlpha(dismissView, 1f); ViewHelper.setTranslationX(dismissView, 0); ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); lp.height = originalHeight; dismissView.setLayoutParams(lp); } }); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //这段代码的效果是ListView删除某item之后,其他的item向上滑动的效果 lp.height = (Integer) valueAnimator.getAnimatedValue(); dismissView.setLayoutParams(lp); } }); }
以下是简单Demo的源码
public class SwipeDismissListView extends ListView { /** * 认为是用户滑动的最小距离 */ private int mSlop; /** * 滑动的最小速度 */ private int mMinFlingVelocity; /** * 滑动的最大速度 */ private int mMaxFlingVelocity; /** * 执行动画的时间 */ protected long mAnimationTime = 150; /** * 用来标记用户是否正在滑动中 */ private boolean mSwiping; /** * 滑动速度检测类 */ private VelocityTracker mVelocityTracker; /** * 手指按下的position */ private int mDownPosition; /** * 按下的item对应的View */ private View mDownView; private float mDownX; private float mDownY; /** * item的宽度 */ private int mViewWidth; /** * 当ListView的Item滑出界面回调的接口 */ private OnDismissCallback onDismissCallback; /** * 设置动画时间 * * @param mAnimationTime */ public void setmAnimationTime(long mAnimationTime) { this.mAnimationTime = mAnimationTime; } /** * 设置删除回调接口 * * @param onDismissCallback */ /** * 删除的回调接口 * * @author xiaanming */ public interface OnDismissCallback { public void onDismiss(int dismissPosition); } public void setOnDismissCallback(OnDismissCallback onDismissCallback) { this.onDismissCallback = onDismissCallback; } public SwipeDismissListView(Context context) { this(context, null); } public SwipeDismissListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwipeDismissListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); ViewConfiguration vc = ViewConfiguration.get(context); mSlop = vc.getScaledTouchSlop(); mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 8; //获取滑动的最小速度 mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); //获取滑动的最大速度 } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: handleActionDown(ev); break; case MotionEvent.ACTION_MOVE: return handleActionMove(ev); case MotionEvent.ACTION_UP: handleActionUp(ev); break; } return super.onTouchEvent(ev); } /** * 按下事件处理 * * @param ev * @return */ private void handleActionDown(MotionEvent ev) { mDownX = ev.getX(); mDownY = ev.getY(); mDownPosition = pointToPosition((int) mDownX, (int) mDownY); if (mDownPosition == AdapterView.INVALID_POSITION) { return; } mDownView = getChildAt(mDownPosition - getFirstVisiblePosition()); if (mDownView != null) { mViewWidth = mDownView.getWidth(); } //加入速度检测 mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); } /** * 处理手指滑动的方法 * * @param ev * @return */ private boolean handleActionMove(MotionEvent ev) { if (mVelocityTracker == null || mDownView == null) { return super.onTouchEvent(ev); } // 获取X方向滑动的距离 float deltaX = ev.getX() - mDownX; float deltaY = ev.getY() - mDownY; // X方向滑动的距离大于mSlop并且Y方向滑动的距离小于mSlop,表示可以滑动 if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop) { mSwiping = true; //当手指滑动item,取消item的点击事件,不然我们滑动Item也伴随着item点击事件的发生 MotionEvent cancelEvent = MotionEvent.obtain(ev); cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); onTouchEvent(cancelEvent); } if (mSwiping) { // 跟谁手指移动item ViewHelper.setTranslationX(mDownView, deltaX); // 透明度渐变 ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth))); // 手指滑动的时候,返回true,表示SwipeDismissListView自己处理onTouchEvent,其他的就交给父类来处理 return true; } return super.onTouchEvent(ev); } /** * 手指抬起的事件处理 * * @param ev */ private void handleActionUp(MotionEvent ev) { if (mVelocityTracker == null || mDownView == null || !mSwiping) { return; } float deltaX = ev.getX() - mDownX; //通过滑动的距离计算出X,Y方向的速度 mVelocityTracker.computeCurrentVelocity(1000); float velocityX = Math.abs(mVelocityTracker.getXVelocity()); float velocityY = Math.abs(mVelocityTracker.getYVelocity()); boolean dismiss = false; //item是否要滑出屏幕 boolean dismissRight = false;//是否往右边删除 //当拖动item的距离大于item的一半,item滑出屏幕 if (Math.abs(deltaX) > mViewWidth / 2) { dismiss = true; dismissRight = deltaX > 0; //手指在屏幕滑动的速度在某个范围内,也使得item滑出屏幕 } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX) { dismiss = true; dismissRight = mVelocityTracker.getXVelocity() > 0; } if (dismiss) { AnimatorSet set = new AnimatorSet(); set.playTogether(ObjectAnimator.ofFloat(mDownView, "translationX", dismissRight ? mViewWidth : -mViewWidth), ObjectAnimator.ofFloat(mDownView, "alpha", 0)); set.setDuration(mAnimationTime).start(); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //Item滑出界面之后执行删除 performDismiss(mDownView, mDownPosition); } }); } else { //将item滑动至开始位置 com.nineoldandroids.view.ViewPropertyAnimator.animate(mDownView) .translationX(0) .alpha(1) .setDuration(mAnimationTime).setListener(null); } //移除速度检测 if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } mSwiping = false; } /** * 在此方法中执行item删除之后,其他的item向上或者向下滚动的动画,并且将position回调到方法onDismiss()中 * * @param dismissView * @param dismissPosition */ private void performDismiss(final View dismissView, final int dismissPosition) { final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();//获取item的布局参数 final int originalHeight = dismissView.getHeight();//item的高度 ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 0).setDuration(mAnimationTime); animator.start(); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (onDismissCallback != null) { onDismissCallback.onDismiss(dismissPosition); } //这段代码很重要,因为我们并没有将item从ListView中移除,而是将item的高度设置为0 //所以我们在动画执行完毕之后将item设置回来 ViewHelper.setAlpha(dismissView, 1f); ViewHelper.setTranslationX(dismissView, 0); ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); lp.height = originalHeight; dismissView.setLayoutParams(lp); } }); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //这段代码的效果是ListView删除某item之后,其他的item向上滑动的效果 lp.height = (Integer) valueAnimator.getAnimatedValue(); dismissView.setLayoutParams(lp); } }); }}
布局文件list_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.cjm.bignews.UI.SwipeDismissListView android:id="@+id/swipdislist" android:layout_width="match_parent" android:layout_height="match_parent"> </com.example.cjm.bignews.UI.SwipeDismissListView></LinearLayout>
Activity 这里就用简单的默认Adapter,用自定义的BaseAdapter都是相似的操作
public class ListActivity extends Activity{ private SwipeDismissListView listView; private List<String> data; private ArrayAdapter<String> adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.list_layout); InitData(); InitView(); InitListener(); } private void InitData(){ data=new ArrayList<String>(); data.add("卡牌大师"); data.add("小鱼人"); data.add("EZ"); data.add("卡特琳娜"); } private void InitView(){ listView=(SwipeDismissListView)findViewById(R.id.swipdislist); adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data); listView.setAdapter(adapter); } private void InitListener(){ listView.setOnDismissCallback(new SwipeDismissListView.OnDismissCallback() { @Override public void onDismiss(int dismissPosition) { data.remove(dismissPosition); adapter.notifyDataSetChanged(); } }); }}
- Android开发之自定义侧滑ListView
- Android开发之自定义的ListView
- Android之--自定义ListView
- Android之自定义ListView
- Android之自定义ListView
- Android开发之自定义控件--ListView的下拉刷新功能
- android开发:自定义ListView总结
- Android开发之ListView
- Android开发之ListView
- android开发之ListView
- Android开发之ListView
- Android开发之ListView
- Android开发之ListView
- android开发之ListView
- android之ListView自定义布局
- Android 自定义ListView Item侧滑删除
- Android开发-DrawerLayout实现侧滑菜单(1)-自定义ListView的实现&沉浸式状态栏
- Android系列之自定义ListView实现左右滑删除
- poj-2485
- mongo shell 高级之 主从复制
- ubuntu下火狐浏览器打不开
- 欢迎使用CSDN-markdown编辑器
- mongo shell 高级之 副本集
- Android开发之自定义侧滑ListView
- Swift截取HTML中的所有图片url
- mongo shell 高级之 分片
- 前序和中序重建二叉树
- mongo shell 高级之 高可用集群
- js script加载
- Python-远程管理-Paramiko实现ssh&sftp
- 浅谈DOS与DDOS攻击的原理
- NOIP2014普及组 T4 子矩阵 DFS+DP