Android仿微信对话列表滑动删除效果

微信对话列表滑动删除效果很不错的,借鉴了github上SwipeListView(项目地址:https://github.com/likebamboo/SwipeListView),在其上进行了一些重构,最终实现了微信对话列表滑动删除效果。

实现原理
 1.通过ListView的pointToPosition(int x, int y)来获取按下的position,然后通过android.view.ViewGroup.getChildAt(position)来得到滑动对象swipeView
 2.在onTouchEvent中计算要滑动的距离,调用swipeView.scrollTo即可。

运行效果如下

下面是最核心的部分SwipeListView代码: 

package com.fxsky.swipelist.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;

import com.fxsky.swipelist.R;

public class SwipeListView extends ListView {
 private Boolean mIsHorizontal;

 private View mPreItemView;

 private View mCurrentItemView;

 private float mFirstX;

 private float mFirstY;

 private int mRightViewWidth;

 // private boolean mIsInAnimation = false;
 private final int mDuration = 100;

 private final int mDurationStep = 10;

 private boolean mIsShown;

 public SwipeListView(Context context) {
 this(context,null);
 }

 public SwipeListView(Context context, AttributeSet attrs) {
 this(context, attrs,0);
 }

 public SwipeListView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 
 TypedArray mTypedArray = context.obtainStyledAttributes(attrs, 
 R.styleable.swipelistviewstyle); 
 
 //获取自定义属性和默认值 
 mRightViewWidth = (int) mTypedArray.getDimension(R.styleable.swipelistviewstyle_right_width, 200); 
 
 mTypedArray.recycle(); 
 }

 /**
 * return true, deliver to listView. return false, deliver to child. if
 * move, return true
 */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
 float lastX = ev.getX();
 float lastY = ev.getY();
 switch (ev.getAction()) {
 case MotionEvent.ACTION_DOWN:
 mIsHorizontal = null;
 System.out.println("onInterceptTouchEvent----->ACTION_DOWN");
 mFirstX = lastX;
 mFirstY = lastY;
 int motionPosition = pointToPosition((int)mFirstX, (int)mFirstY);

 if (motionPosition >= 0) {
  View currentItemView = getChildAt(motionPosition - getFirstVisiblePosition());
  mPreItemView = mCurrentItemView;
  mCurrentItemView = currentItemView;
 }
 break;

 case MotionEvent.ACTION_MOVE:
 float dx = lastX - mFirstX;
 float dy = lastY - mFirstY;

 if (Math.abs(dx) >= 5 && Math.abs(dy) >= 5) {
  return true;
 }
 break;

 case MotionEvent.ACTION_UP:
 case MotionEvent.ACTION_CANCEL:
 System.out.println("onInterceptTouchEvent----->ACTION_UP");
 if (mIsShown && (mPreItemView != mCurrentItemView || isHitCurItemLeft(lastX))) {
  System.out.println("1---> hiddenRight");
  /**
  * 情况一:
  * <p>
  * 一个Item的右边布局已经显示,
  * <p>
  * 这时候点击任意一个item, 那么那个右边布局显示的item隐藏其右边布局
  */
  hiddenRight(mPreItemView);
 }
 break;
 }

 return super.onInterceptTouchEvent(ev);
 }

 private boolean isHitCurItemLeft(float x) {
 return x < getWidth() - mRightViewWidth;
 }

 /**
 * @param dx
 * @param dy
 * @return judge if can judge scroll direction
 */
 private boolean judgeScrollDirection(float dx, float dy) {
 boolean canJudge = true;

 if (Math.abs(dx) > 30 && Math.abs(dx) > 2 * Math.abs(dy)) {
 mIsHorizontal = true;
 System.out.println("mIsHorizontal---->" + mIsHorizontal);
 } else if (Math.abs(dy) > 30 && Math.abs(dy) > 2 * Math.abs(dx)) {
 mIsHorizontal = false;
 System.out.println("mIsHorizontal---->" + mIsHorizontal);
 } else {
 canJudge = false;
 }

 return canJudge;
 }

 /**
 * return false, can't move any direction. return true, cant't move
 * vertical, can move horizontal. return super.onTouchEvent(ev), can move
 * both.
 */
 @Override
 public boolean onTouchEvent(MotionEvent ev) {
 float lastX = ev.getX();
 float lastY = ev.getY();

 switch (ev.getAction()) {
 case MotionEvent.ACTION_DOWN:
 System.out.println("---->ACTION_DOWN");
 break;

 case MotionEvent.ACTION_MOVE:
 float dx = lastX - mFirstX;
 float dy = lastY - mFirstY;

 // confirm is scroll direction
 if (mIsHorizontal == null) {
  if (!judgeScrollDirection(dx, dy)) {
  break;
  }
 }

 if (mIsHorizontal) {
  if (mIsShown && mPreItemView != mCurrentItemView) {
  System.out.println("2---> hiddenRight");
  /**
  * 情况二:
  * <p>
  * 一个Item的右边布局已经显示,
  * <p>
  * 这时候左右滑动另外一个item,那个右边布局显示的item隐藏其右边布局
  * <p>
  * 向左滑动只触发该情况,向右滑动还会触发情况五
  */
  hiddenRight(mPreItemView);
  }

  if (mIsShown && mPreItemView == mCurrentItemView) {
  dx = dx - mRightViewWidth;
  System.out.println("======dx " + dx);
  }

  // can't move beyond boundary
  if (dx < 0 && dx > -mRightViewWidth) {
  mCurrentItemView.scrollTo((int)(-dx), 0);
  }

  return true;
 } else {
  if (mIsShown) {
  System.out.println("3---> hiddenRight");
  /**
  * 情况三:
  * <p>
  * 一个Item的右边布局已经显示,
  * <p>
  * 这时候上下滚动ListView,那么那个右边布局显示的item隐藏其右边布局
  */
  hiddenRight(mPreItemView);
  }
 }

 break;

 case MotionEvent.ACTION_UP:
 case MotionEvent.ACTION_CANCEL:
 System.out.println("============ACTION_UP");
 clearPressedState();
 if (mIsShown) {
  System.out.println("4---> hiddenRight");
  /**
  * 情况四:
  * <p>
  * 一个Item的右边布局已经显示,
  * <p>
  * 这时候左右滑动当前一个item,那个右边布局显示的item隐藏其右边布局
  */
  hiddenRight(mPreItemView);
 }

 if (mIsHorizontal != null && mIsHorizontal) {
  if (mFirstX - lastX > mRightViewWidth / 2) {
  showRight(mCurrentItemView);
  } else {
  System.out.println("5---> hiddenRight");
  /**
  * 情况五:
  * <p>
  * 向右滑动一个item,且滑动的距离超过了右边View的宽度的一半,隐藏之。
  */
  hiddenRight(mCurrentItemView);
  }

  return true;
 }

 break;
 }

 return super.onTouchEvent(ev);
 }

 private void clearPressedState() {
 // TODO current item is still has background, issue
 mCurrentItemView.setPressed(false);
 setPressed(false);
 refreshDrawableState();
 // invalidate();
 }

 private void showRight(View view) {
 System.out.println("=========showRight");

 Message msg = new MoveHandler().obtainMessage();
 msg.obj = view;
 msg.arg1 = view.getScrollX();
 msg.arg2 = mRightViewWidth;
 msg.sendToTarget();

 mIsShown = true;
 }

 private void hiddenRight(View view) {
 System.out.println("=========hiddenRight");
 if (mCurrentItemView == null) {
 return;
 }
 Message msg = new MoveHandler().obtainMessage();//
 msg.obj = view;
 msg.arg1 = view.getScrollX();
 msg.arg2 = 0;

 msg.sendToTarget();

 mIsShown = false;
 }

 /**
 * show or hide right layout animation
 */
 @SuppressLint("HandlerLeak")
 class MoveHandler extends Handler {
 int stepX = 0;

 int fromX;

 int toX;

 View view;

 private boolean mIsInAnimation = false;

 private void animatioOver() {
 mIsInAnimation = false;
 stepX = 0;
 }

 @Override
 public void handleMessage(Message msg) {
 super.handleMessage(msg);
 if (stepX == 0) {
 if (mIsInAnimation) {
  return;
 }
 mIsInAnimation = true;
 view = (View)msg.obj;
 fromX = msg.arg1;
 toX = msg.arg2;
 stepX = (int)((toX - fromX) * mDurationStep * 1.0 / mDuration);
 if (stepX < 0 && stepX > -1) {
  stepX = -1;
 } else if (stepX > 0 && stepX < 1) {
  stepX = 1;
 }
 if (Math.abs(toX - fromX) < 10) {
  view.scrollTo(toX, 0);
  animatioOver();
  return;
 }
 }

 fromX += stepX;
 boolean isLastStep = (stepX > 0 && fromX > toX) || (stepX < 0 && fromX < toX);
 if (isLastStep) {
 fromX = toX;
 }

 view.scrollTo(fromX, 0);
 invalidate();

 if (!isLastStep) {
 this.sendEmptyMessageDelayed(0, mDurationStep);
 } else {
 animatioOver();
 }
 }
 }

 public int getRightViewWidth() {
 return mRightViewWidth;
 }

 public void setRightViewWidth(int mRightViewWidth) {
 this.mRightViewWidth = mRightViewWidth;
 }
}

Demo下载地址:http://xiazai.jb51.net/201608/yuanma/SwipeListView(jb51.net).rar

Demo中SwipeAdapter源码中有一处由于粗心写错了,会导致向下滑动时出现数组越界异常,现更正如下:

@Override
 public int getCount() {
// return 100;
 return data.size();
 }

本文已被整理到了《Android微信开发教程汇总》,欢迎大家学习阅读。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持菜鸟教程(cainiaojc.com)。

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#cainiaojc.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。