package ml.puredark.hviewer.libraries.swipeback.dispatchactivity; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import ml.puredark.hviewer.libraries.swipeback.common.SwipeBackApplication; /** * Created by fhf11991 on 2016/9/18. */ public class SwipeWindowHelper extends Handler { private static final String TAG = "SwipeWindowHelper"; private static final String CURRENT_POINT_X = "currentPointX"; //点击事件 private static final int MSG_ACTION_DOWN = 1; //点击事件 private static final int MSG_ACTION_MOVE = 2; //滑动事件 private static final int MSG_ACTION_UP = 3; //点击结束 private static final int MSG_SLIDE_CANCEL = 4; //开始滑动,不返回前一个页面 private static final int MSG_SLIDE_CANCELED = 5; //结束滑动,不返回前一个页面 private static final int MSG_SLIDE_PROCEED = 6; //开始滑动,返回前一个页面 private static final int MSG_SLIDE_FINISHED = 7;//结束滑动,返回前一个页面 private static final int SHADOW_WIDTH = 50; //px 阴影宽度 private static final int MARGIN_THRESHOLD = 40; //px 默认拦截手势区间 0~40 private final FrameLayout mCurrentContentView; private boolean mIsSliding; //是否正在滑动 private boolean mIsSlideAnimPlaying; //滑动动画展示过程中 private float mDistanceX; //px 当前滑动距离 (正数或0) private int mMarginThreshold; //px 拦截手势区间 private float mLastPointX; //记录手势在屏幕上的X轴坐标 private boolean mIsSupportSlideBack; // private Window mCurrentWindow; private ViewManager mViewManager; public SwipeWindowHelper(@NonNull Window window) { this(window, true); } public SwipeWindowHelper(@NonNull Window window, boolean isSupportSlideBack) { mCurrentWindow = window; mIsSupportSlideBack = isSupportSlideBack; mCurrentContentView = getContentView(mCurrentWindow); mViewManager = new ViewManager(); } public void setSupportSlideBack(boolean supportSlideBack) { mIsSupportSlideBack = supportSlideBack; } public boolean processTouchEvent(MotionEvent ev) { if (!mIsSupportSlideBack) { //不支持滑动返回,则手势事件交给View处理 return false; } if (mIsSlideAnimPlaying) { //正在滑动动画播放中,直接消费手势事件 return true; } if (mMarginThreshold == 0) { //动态设置滑动拦截事件的区域 final int commonMargin = 45; mMarginThreshold = Math.min(MARGIN_THRESHOLD, commonMargin); } final int action = ev.getAction() & MotionEvent.ACTION_MASK; final int actionIndex = ev.getActionIndex(); switch (action) { case MotionEvent.ACTION_DOWN: mLastPointX = ev.getRawX(); boolean inThresholdArea = mLastPointX >= 0 && mLastPointX <= mMarginThreshold; if (inThresholdArea && mIsSliding) { return true; } else if (inThresholdArea && !mIsSliding) { //开始滑动 mIsSliding = true; sendEmptyMessage(MSG_ACTION_DOWN); return true; } break; case MotionEvent.ACTION_POINTER_DOWN: if (mIsSliding) { //有第二个手势事件加入,而且正在滑动事件中,则直接消费事件 return true; } break; case MotionEvent.ACTION_MOVE: if (mIsSliding && actionIndex == 0) { //开始滑动 Message message = obtainMessage(); Bundle bundle = new Bundle(); bundle.putFloat(CURRENT_POINT_X, ev.getRawX()); message.what = MSG_ACTION_MOVE; message.setData(bundle); sendMessage(message); return true; } else if (mIsSliding && actionIndex != 0) { return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_OUTSIDE: if (mIsSliding && actionIndex == 0) { // 取消滑动 或 手势抬起 ,而且手势事件是第一手势,开始滑动动画 mIsSliding = false; sendEmptyMessage(MSG_ACTION_UP); return true; } else if (mIsSliding && actionIndex != 0) { return true; } break; default: mIsSliding = false; break; } return false; } public Context getContext() { return mCurrentWindow == null ? null : mCurrentWindow.getContext(); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case MSG_ACTION_DOWN: // hide input method InputMethodManager inputMethod = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); View view = mCurrentWindow.getCurrentFocus(); if (view != null) { inputMethod.hideSoftInputFromWindow(view.getWindowToken(), 0); } if (!mViewManager.addViewFromPreviousActivity()) return; // add shadow view on the left of content view mViewManager.addShadowView(); if (mCurrentContentView.getChildCount() >= 3) { View curView = mViewManager.getDisplayView(); if (curView.getBackground() == null) { int color = getWindowBackgroundColor(); curView.setBackgroundColor(color); } } break; case MSG_ACTION_MOVE: final float curPointX = msg.getData().getFloat(CURRENT_POINT_X); onSliding(curPointX); break; case MSG_ACTION_UP: final int width = getContext().getResources().getDisplayMetrics().widthPixels; if (mDistanceX == 0) { if (mCurrentContentView.getChildCount() >= 3) { mViewManager.removeShadowView(); mViewManager.resetPreviousView(); } } else if (mDistanceX > width / 4) { sendEmptyMessage(MSG_SLIDE_PROCEED); } else { sendEmptyMessage(MSG_SLIDE_CANCEL); } break; case MSG_SLIDE_CANCEL: startSlideAnim(true); break; case MSG_SLIDE_CANCELED: mDistanceX = 0; mIsSliding = false; mViewManager.removeShadowView(); mViewManager.resetPreviousView(); break; case MSG_SLIDE_PROCEED: startSlideAnim(false); break; case MSG_SLIDE_FINISHED: mViewManager.addCacheView(); mViewManager.removeShadowView(); mViewManager.resetPreviousView(); if (getContext() instanceof Activity) { Activity activity = (Activity) getContext(); activity.finish(); activity.overridePendingTransition(0, 0); } else if (getContext() instanceof ContextWrapper) { Context baseContext = ((ContextWrapper) getContext()).getBaseContext(); if (baseContext instanceof Activity) { Activity activity = (Activity) baseContext; activity.finish(); activity.overridePendingTransition(0, 0); } } break; default: break; } } private int getWindowBackgroundColor() { TypedArray array = null; try { array = getContext().getTheme().obtainStyledAttributes(new int[]{android.R.attr.windowBackground}); return array.getColor(0, ContextCompat.getColor(getContext(), android.R.color.transparent)); } finally { if (array != null) { array.recycle(); } } } /** * 手动处理滑动事件 */ private synchronized void onSliding(float curPointX) { final int width = getContext().getResources().getDisplayMetrics().widthPixels; View previewActivityContentView = mViewManager.mPreviousContentView; View shadowView = mViewManager.mShadowView; View currentActivityContentView = mViewManager.getDisplayView(); if (previewActivityContentView == null || currentActivityContentView == null || shadowView == null) { sendEmptyMessage(MSG_SLIDE_CANCELED); return; } final float distanceX = curPointX - mLastPointX; mLastPointX = curPointX; mDistanceX = mDistanceX + distanceX; if (mDistanceX < 0) { mDistanceX = 0; } previewActivityContentView.setX(-width / 3 + mDistanceX / 3); shadowView.setX(mDistanceX - SHADOW_WIDTH); currentActivityContentView.setX(mDistanceX); } /** * 开始自动滑动动画 * * @param slideCanceled 是不是要返回(true则不关闭当前页面) */ private void startSlideAnim(final boolean slideCanceled) { final View previewView = mViewManager.mPreviousContentView; final View shadowView = mViewManager.mShadowView; final View currentView = mViewManager.getDisplayView(); if (previewView == null || currentView == null) { return; } int width = getContext().getResources().getDisplayMetrics().widthPixels; Interpolator interpolator = new DecelerateInterpolator(2f); // preview activity's animation ObjectAnimator previewViewAnim = new ObjectAnimator(); previewViewAnim.setInterpolator(interpolator); previewViewAnim.setProperty(View.TRANSLATION_X); float preViewStart = mDistanceX / 3 - width / 3; float preViewStop = slideCanceled ? -width / 3 : 0; previewViewAnim.setFloatValues(preViewStart, preViewStop); previewViewAnim.setTarget(previewView); // shadow view's animation ObjectAnimator shadowViewAnim = new ObjectAnimator(); shadowViewAnim.setInterpolator(interpolator); shadowViewAnim.setProperty(View.TRANSLATION_X); float shadowViewStart = mDistanceX - SHADOW_WIDTH; float shadowViewEnd = slideCanceled ? SHADOW_WIDTH : width + SHADOW_WIDTH; shadowViewAnim.setFloatValues(shadowViewStart, shadowViewEnd); shadowViewAnim.setTarget(shadowView); // current view's animation ObjectAnimator currentViewAnim = new ObjectAnimator(); currentViewAnim.setInterpolator(interpolator); currentViewAnim.setProperty(View.TRANSLATION_X); float curViewStart = mDistanceX; float curViewStop = slideCanceled ? 0 : width; currentViewAnim.setFloatValues(curViewStart, curViewStop); currentViewAnim.setTarget(currentView); // play animation together AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(slideCanceled ? 150 : 300); animatorSet.playTogether(previewViewAnim, shadowViewAnim, currentViewAnim); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (slideCanceled) { mIsSlideAnimPlaying = false; previewView.setX(0); shadowView.setX(-SHADOW_WIDTH); currentView.setX(0); sendEmptyMessage(MSG_SLIDE_CANCELED); } else { sendEmptyMessage(MSG_SLIDE_FINISHED); } } }); animatorSet.start(); mIsSlideAnimPlaying = true; } private final FrameLayout getContentView(Window window) { if (window == null) return null; return (FrameLayout) window.findViewById(Window.ID_ANDROID_CONTENT); } class ViewManager { private Activity mPreviousActivity; private ViewGroup mPreviousContentView; private View mShadowView; /** * Remove view from previous Activity and add into current Activity * * @return Is view added successfully */ private boolean addViewFromPreviousActivity() { if (mCurrentContentView.getChildCount() == 0) { mPreviousActivity = null; mPreviousContentView = null; return false; } SwipeBackApplication application = (SwipeBackApplication) mCurrentWindow.getContext().getApplicationContext(); mPreviousActivity = application.getActivityLifecycleHelper().getPreActivity(); if (mPreviousActivity == null) { mPreviousActivity = null; mPreviousContentView = null; return false; } ViewGroup previousActivityContainer = getContentView(mPreviousActivity.getWindow()); if (previousActivityContainer.getChildAt(0) instanceof ShadowView) { previousActivityContainer.removeViewAt(0); } if (previousActivityContainer == null || previousActivityContainer.getChildCount() == 0 || !(previousActivityContainer.getChildAt(0) instanceof ViewGroup)) { mPreviousActivity = null; mPreviousContentView = null; return false; } mPreviousContentView = (ViewGroup) previousActivityContainer.getChildAt(0); previousActivityContainer.removeView(mPreviousContentView); mCurrentContentView.addView(mPreviousContentView, 0); return true; } /** * Remove the PreviousContentView at current Activity and put it into previous Activity. */ private void resetPreviousView() { if (mPreviousContentView == null) return; View view = mPreviousContentView; FrameLayout contentView = mCurrentContentView; view.setX(0); contentView.removeView(view); mPreviousContentView = null; if (mPreviousActivity == null || mPreviousActivity.isFinishing()) return; Activity preActivity = mPreviousActivity; final ViewGroup previewContentView = getContentView(preActivity.getWindow()); previewContentView.addView(view, 0); mPreviousActivity = null; } /** * add shadow view on the left of content view */ private void addShadowView() { if (mShadowView == null) { mShadowView = new ShadowView(getContext()); mShadowView.setX(-SHADOW_WIDTH); } final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( SHADOW_WIDTH, FrameLayout.LayoutParams.MATCH_PARENT); final FrameLayout contentView = mCurrentContentView; if (mShadowView.getParent() == null) contentView.addView(mShadowView, 1, layoutParams); } private void removeShadowView() { if (mShadowView == null) return; final FrameLayout contentView = getContentView(mCurrentWindow); contentView.removeView(mShadowView); mShadowView = null; } private void addCacheView() { final FrameLayout contentView = mCurrentContentView; final View previousView = mPreviousContentView; PreviousPageView previousPageView = new PreviousPageView(getContext()); contentView.addView(previousPageView, 0); previousPageView.cacheView(previousView); } private View getDisplayView() { int index = 0; if (mViewManager.mPreviousContentView != null) { index = index + 1; } if (mViewManager.mShadowView != null) { index = index + 1; } return mCurrentContentView.getChildAt(index); } } } class PreviousPageView extends View { private View mView; public PreviousPageView(Context context) { super(context); } public void cacheView(View view) { mView = view; invalidate(); } @Override protected void onDraw(Canvas canvas) { if (mView != null) { mView.draw(canvas); mView = null; } } } class ShadowView extends View { private Drawable mDrawable; public ShadowView(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mDrawable == null) { int colors[] = {0x00000000, 0x17000000, 0x43000000};//分别为开始颜色,中间夜色,结束颜色 mDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors); } mDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); mDrawable.draw(canvas); } }