package com.roboo.like.netease.view;
import android.R.anim;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.ListView;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;
import com.nineoldandroids.view.ViewHelper;
public class SwipeListView extends ListView implements OnTouchListener
{
private int mSlop;
/** 最小手指滑动速率 */
private int mMinFlingVelocity;
/** 最大手指滑动速率 */
private int mMaxFlingVelocity;
/** 滑动动画持续时间 */
private long mAnimationDurationTime;
/** 手指触摸屏幕时记录X位置 */
private float mDownX;
/** 是否在滑动的标志 */
private boolean mIsSwipping;
/** 移动速率测量器 */
private VelocityTracker mVelocityTracker;
/** 手指按下时对应View的位置 */
private int mDownPosition;
/** 手指按下时对应的View */
private View mDownView;
private OnDismissCallback mCallback;
private int mViewWidth = 1;
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);
init();
}
public void init()
{
ViewConfiguration configuration = ViewConfiguration.get(getContext());
mSlop = configuration.getScaledTouchSlop();
mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = configuration.getScaledMaximumFlingVelocity();
mAnimationDurationTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
}
@Override
public boolean onTouch(View v, MotionEvent motionEvent)
{
if (mViewWidth < 2)
{
mViewWidth = getWidth();
}
switch (motionEvent.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
Rect rect = new Rect();
int childCount = getChildCount();
int[] coords = new int[2];
getLocationOnScreen(coords);
int x = (int) (motionEvent.getRawX() - coords[0]);
int y = (int) (motionEvent.getRawY() - coords[1]);
View childView;
for (int i = 0; i < childCount; i++)
{
childView = getChildAt(i);
childView.getHitRect(rect);
if (rect.contains(x, y))
{
mDownView = childView;
break;
}
}
if (null != mDownView)
{
mDownX = motionEvent.getRawX();
mDownPosition = getPositionForView(mDownView);
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(motionEvent);
}
v.onTouchEvent(motionEvent);
return true;
case MotionEvent.ACTION_UP:
{
if (mVelocityTracker == null)
{
break;
}
float deltaX = motionEvent.getRawX() - mDownX;
mVelocityTracker.addMovement(motionEvent);
mVelocityTracker.computeCurrentVelocity(1000);
float velocityX = Math.abs(mVelocityTracker.getXVelocity());
float velocityY = Math.abs(mVelocityTracker.getYVelocity());
boolean dismiss = false;
boolean dismissRight = false;
if (Math.abs(deltaX) > mViewWidth / 2)
{
dismiss = true;
dismissRight = deltaX > 0;
}
//当在X方向上的速率大于最小阀值,并且小于最大阀值,并且在大于在Y方向上的速率时,认为满足X方向滑动操作
else if(mMinFlingVelocity <=velocityX && velocityX <=mMaxFlingVelocity && velocityY < velocityX)
{
dismiss = true;
dismissRight = mVelocityTracker.getXVelocity() > 0;
}
if(dismiss)
{
com.nineoldandroids.view.ViewPropertyAnimator.animate(mDownView).translationX(dismissRight?mViewWidth:-mViewWidth)
.alpha(0).setDuration(mAnimationDurationTime)
.setListener(new com.nineoldandroids.animation.AnimatorListenerAdapter()
{
public void onAnimationEnd(Animator animation) {
performDismiss(mDownView, mDownPosition);
}
});
}
else
{
com.nineoldandroids.view.ViewPropertyAnimator.animate(mDownView).translationX(0)
.alpha(1).setDuration(mAnimationDurationTime).setListener(null);
}
mVelocityTracker = null;
mDownX =0;
mDownView =null;
mDownPosition = ListView.INVALID_POSITION;
mIsSwipping = false;
}
break;
case MotionEvent.ACTION_MOVE:
{
if(mVelocityTracker == null)
{
break;
}
mVelocityTracker.addMovement(motionEvent);
float deltaX = motionEvent.getRawX() - mDownX;
if(Math.abs(deltaX) > mSlop)
{
mIsSwipping = true;
requestDisallowInterceptTouchEvent(true);
MotionEvent cancleEvent = MotionEvent.obtain(motionEvent);
cancleEvent.setAction(MotionEvent.ACTION_CANCEL|(motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
onTouchEvent(cancleEvent);
}
if(mIsSwipping)
{
ViewHelper.setTranslationX(mDownView, deltaX);
ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f,
1f - 2f * Math.abs(deltaX) / mViewWidth)));
return true;
}
break;
}
default:
break;
}
return false;
}
private void performDismiss(final View dismissView ,final int dismissPosition)
{
final ViewGroup.LayoutParams params = dismissView.getLayoutParams();
final int originHeight = dismissView.getHeight();
ValueAnimator animator = ValueAnimator.ofInt(originHeight,1).setDuration(mAnimationDurationTime);
animator.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(Animator animation)
{
super.onAnimationEnd(animation);
}
});
animator.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
params.height = (Integer) animation.getAnimatedValue();
dismissView.setLayoutParams(params);
}
});
animator.start();
}
public void setOnDismissCallback(OnDismissCallback callback)
{
this.mCallback = callback;
}
public interface OnDismissCallback
{
public void onDismiss();
}
}