package org.zywx.wbpalmstar.base.view; import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.os.Build; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; @SuppressLint("NewApi") public class SwipeView extends FrameLayout { public static final String TAG = SwipeView.class.getSimpleName(); public static boolean sNavFlag = false;//设置全部Window是否可以滑动 /** * 是否可以滑动关闭页面 */ protected boolean mSwipeEnabled = true; /** * 是否可以在页面任意位置右滑关闭页面,如果是false则从左边滑才可以关闭。 */ protected boolean mSwipeAnyWhere = false; boolean mCanSwipe = false; /** * 超过了touchslop仍然没有达到没有条件,则忽略以后的动作 */ boolean mIgnoreSwipe = false; int mSideWidthInDP = 16; int mSideWidth = 72; int mScreenWidth = 1080; VelocityTracker mVelocityTracker; float mDownX; float mDownY; float mLastX; float mCurrentX; float mCurrentY; int mTouchSlopDP = 30; int mTouchSlop = 60; boolean mAbleToSwipe = true;//控制每个Window是否可以滑动(root Window不能滑动) private OnViewClosedListener mOnViewClosedListener = null; public SwipeView(Context context) { super(context); init(); } public SwipeView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public void setSwipeAnyWhere(boolean swipeAnyWhere) { this.mSwipeAnyWhere = swipeAnyWhere; } public boolean isSwipeAnyWhere() { return mSwipeAnyWhere; } public void setSwipeEnabled(boolean swipeEnabled) { this.mSwipeEnabled = swipeEnabled; } public boolean isSwipeEnabled() { return mSwipeEnabled; } public static int getScreenWidth(Context context) { DisplayMetrics metrics = new DisplayMetrics(); WindowManager manager = (WindowManager) context.getSystemService(Activity.WINDOW_SERVICE); manager.getDefaultDisplay().getMetrics(metrics); return metrics.widthPixels; } private boolean swipeFinished = false; public void init() { mTouchSlop = (int) (mTouchSlopDP * getContext().getResources().getDisplayMetrics().density); mSideWidth = (int) (mSideWidthInDP * getContext().getResources().getDisplayMetrics().density); mScreenWidth = getScreenWidth(getContext()); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mAbleToSwipe && sNavFlag && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (mSwipeEnabled && !mCanSwipe && !mIgnoreSwipe) { if (mSwipeAnyWhere) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = ev.getRawX(); mDownY = ev.getRawY(); mCurrentX = mDownX; mCurrentY = mDownY; mLastX = mDownX; break; case MotionEvent.ACTION_MOVE: float dx = ev.getRawX() - mDownX; float dy = ev.getRawY() - mDownY; if (dx * dx + dy * dy > mTouchSlop * mTouchSlop) { if (dy == 0f || Math.abs(dx / dy) > 1) { mDownX = ev.getRawX(); mDownY = ev.getRawY(); mCurrentX = mDownX; mCurrentY = mDownY; mLastX = mDownX; mCanSwipe = true; mVelocityTracker = VelocityTracker.obtain(); return true; } else { mIgnoreSwipe = true; } } break; } } else if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getRawX() < mSideWidth) { mCanSwipe = true; mVelocityTracker = VelocityTracker.obtain(); return true; } } if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { mIgnoreSwipe = false; } } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mCanSwipe || super.onInterceptTouchEvent(ev); } boolean hasIgnoreFirstMove; @Override public boolean onTouchEvent(MotionEvent event) { if (mCanSwipe) { mVelocityTracker.addMovement(event); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mDownX = event.getRawX(); mDownY = event.getRawY(); mCurrentX = mDownX; mCurrentY = mDownY; mLastX = mDownX; break; case MotionEvent.ACTION_MOVE: mCurrentX = event.getRawX(); mCurrentY = event.getRawY(); float dx = mCurrentX - mLastX; if (dx != 0f && !hasIgnoreFirstMove) { hasIgnoreFirstMove = true; dx = dx / dx; } if (getContentX() + dx < 0) { setContentX(0); } else { Log.i(TAG, "dx: " + dx); setContentX(mCurrentX); } mLastX = mCurrentX; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mVelocityTracker.computeCurrentVelocity(10000); mVelocityTracker.computeCurrentVelocity(1000, 20000); mCanSwipe = false; hasIgnoreFirstMove = false; int mv = mScreenWidth / 200 * 1000; if (Math.abs(mVelocityTracker.getXVelocity()) > mv) { animateFromVelocity(mVelocityTracker.getXVelocity()); } else { if (getContentX() > mScreenWidth / 2) { animateFinish(false); } else { animateBack(false); } } mVelocityTracker.recycle(); break; default: break; } } return super.onTouchEvent(event); } ObjectAnimator animator; public void cancelPotentialAnimation() { if (animator != null) { animator.removeAllListeners(); animator.cancel(); } } public void setContentX(float x) { int ix = (int) x; Log.i(TAG, "ContentX: " + ix); this.setX(ix); invalidate(); } public float getContentX() { return this.getX(); } /** * 弹回,不关闭,因为left是0,所以setX和setTranslationX效果是一样的 * * @param withVel 使用计算出来的时间 */ private void animateBack(boolean withVel) { cancelPotentialAnimation(); animator = ObjectAnimator.ofFloat(this, "contentX", getContentX(), 0); int tmpDuration = withVel ? ((int) (duration * getContentX() / mScreenWidth)) : duration; if (tmpDuration < 100) { tmpDuration = 100; } animator.setDuration(tmpDuration); animator.setInterpolator(new DecelerateInterpolator()); animator.start(); } private void animateFinish(boolean withVel) { cancelPotentialAnimation(); animator = ObjectAnimator.ofFloat(this, "contentX", getContentX(), mScreenWidth); int tmpDuration = withVel ? ((int) (duration * (mScreenWidth - getContentX()) / mScreenWidth)) : duration; if (tmpDuration < 100) { tmpDuration = 100; } animator.setDuration(tmpDuration); animator.setInterpolator(new DecelerateInterpolator()); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { swipeFinished = true; if (mOnViewClosedListener != null) { mOnViewClosedListener.onViewClosed(); } } @Override public void onAnimationCancel(Animator animation) { } }); animator.start(); } private final int duration = 200; private void animateFromVelocity(float v) { if (v > 0) { if (getContentX() < mScreenWidth / 2 && v * duration / 1000 + getContentX() < mScreenWidth / 2) { animateBack(false); } else { animateFinish(true); } } else { if (getContentX() > mScreenWidth / 2 && v * duration / 1000 + getContentX() > mScreenWidth / 2) { animateFinish(false); } else { animateBack(true); } } } public void setOnViewClosedListener(OnViewClosedListener onViewClosedListener) { mOnViewClosedListener = onViewClosedListener; } public interface OnViewClosedListener { void onViewClosed(); } public void setAbleToSwipe(boolean ableToSwipe) { this.mAbleToSwipe = ableToSwipe; } }