您好,登錄后才能下訂單哦!
前言
首次通過右滑來返回到上一個頁面的操作是在 IOS7上出現。到目前android應用上支持這種操作的依然不多。分析其主要原因應該是android已有實體的返回按鍵,這樣的功能變得不重要,但我覺得有這樣的功能便于單手操作,能提升app的用戶體驗,特別是從ios轉到android的用戶。寫這篇博文希望可以對大家有所幫助,希望自己的app上有滑動返回功能的可以參考下。
原理的簡單描述
Android系統里有很多滑動相關的API和類,比如ViewDragHelper就是一個很好的滑動助手類。首先設置Window的背景為透明,再通過ViewDragHelper對Activity上DecorView的子view進行滑動,當滑動到一定距離,手指離開后就自動滑到最右側,然后finish當前的activity,這樣即可實現滑動返回效果。為了能夠 “全局的”、“聯動的” 實現滑動返回效果,在每個activity的DecorView下插入了SwipeBackLayout,當前activity滑動和下層activity的聯動都在該類中完成。
效果圖
布局圖
實現主要類:
SwipeBackActivity //滑動返回基類
SwipeBackLayout //滑動返回布局類
SwipeBackLayoutDragHelper //修改ViewDragHelper后助手類
TranslucentHelper //代碼中修改透明或者不透明的助手類
##代碼層面的講解
一. 設置activity為透明、activity跳轉動畫(TranslucentHelper 講解)
這個看起來很簡單,但如果要兼容到API16及以下,會遇到過一個比較麻煩的頁面切換動畫問題:
1.1、通過activity的主題style進行設置
<item name="android:windowBackground">@color/transparent</item> <item name="android:windowIsTranslucent">true</item>``` **遇到問題:**如果在某個activity的主題style中設置了android:windowIsTranslucent屬性為true,那么該activity切換動畫與沒設置之前是不同的,有些手機切換動畫會變得非常跳。所以需要自定義activity的切換動畫。 接下來我們會想到通過主題style里的windowAnimationStyle來設置切換動畫
@anim/activity_open_enter
@anim/activity_open_exit
@anim/activity_close_enter
@anim/activity_close_exit```
**實踐證明:**當android:windowIsTranslucent為true時,以上幾個屬性是無效的,而下面兩個屬性還是可以用。但是這兩個屬性一個是窗口進來動畫,一個是窗口退出動畫,明顯是不夠。
<item name="android:windowEnterAnimation">@anim/***</item> <item name="android:windowExitAnimation">@anim/***</item>``
結合overridePendingTransition(int enterAnim, int exitAnim)可以復寫窗口進來動畫和窗口退出動畫,這種我覺得最終可能是可以實現的,不過控制起來比較復雜:
比如有A、B、C三個頁面:
A跳到B,進場頁面B動畫從右進來,出場頁面A動畫從左出去,可以直接在style中寫死
@anim/***
@anim/***```
如果B返回到A,進場頁面A動畫從左進來,出場頁面B動畫從右出去,此時需要通過復寫onBackPressed() 方法,
在其中添加overridePendingTransition(int enterAnim, int exitAnim)方法來改變動畫。
如果B是finish()后到A頁面,在finish()后面加上overridePendingTransition ……
由于onBackPressed() 方法最終會調finish(),所以實際上只需要復寫finish(),在其中添加overridePendingTransition……
但是假如B finish()后跳到C,則又不應該執行overridePendingTransition……,那么就需要判斷finish執行后是否要加 overridePendingTransition……
對于一個較為龐大的項目,采取這種方法需要對每個頁面進行排查,因此是不可行的,而對于剛剛起步的應用來說則是一個選擇。
1.2、通過透明助手類(TranslucentHelper)進行設置
透明助手類(TranslucentHelper)里主要又有兩個方法,一個是讓activity變不透明,一個是讓activity變透明,這兩個都是通過反射來調用隱藏的系統api來實現的。因為較低的版本不支持代碼中修改背景透明不透明,所以在類中有個靜態變量mTranslucentState 來記錄是否可以切換背景,這樣低版本就不需要每次都反射通過捕獲到的異常來做兼容方案。
另外:發現有些手機支持背景變黑,但不支持背景變透明(中興z9 mini 5.0.2系統)
public class TranslucentHelper { private static final String TRANSLUCENT_STATE = "translucentState"; private static final int INIT = 0;//表示初始 private static final int CHANGE_STATE_FAIL = INIT + 1;//表示確認不可以切換透明狀態 private static final int CHANGE_STATE_SUCCEED = CHANGE_STATE_FAIL + 1;//表示確認可以切換透明狀態 private static int mTranslucentState = INIT; interface TranslucentListener { void onTranslucent(); } private static class MyInvocationHandler implements InvocationHandler { private TranslucentListener listener; MyInvocationHandler(TranslucentListener listener) { this.listener = listener; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { boolean success = (boolean) args[0]; if (success && listener != null) { listener.onTranslucent(); } } catch (Exception ignored) { } return null; } } static boolean convertActivityFromTranslucent(Activity activity) { if (mTranslucentState == INIT) { mTranslucentState = PreferencesUtils.getInt(TRANSLUCENT_STATE, INIT); } if (mTranslucentState == INIT) { convertActivityToTranslucent(activity, null); } else if (mTranslucentState == CHANGE_STATE_FAIL) { return false; } try { Method method = Activity.class.getDeclaredMethod("convertFromTranslucent"); method.setAccessible(true); method.invoke(activity); mTranslucentState = CHANGE_STATE_SUCCEED; return true; } catch (Throwable t) { mTranslucentState = CHANGE_STATE_FAIL; PreferencesUtils.saveInt(TRANSLUCENT_STATE, CHANGE_STATE_FAIL); return false; } } static void convertActivityToTranslucent(Activity activity, final TranslucentListener listener) { if (mTranslucentState == CHANGE_STATE_FAIL) { if (listener != null) { listener.onTranslucent(); } return; } try { Class<?>[] classes = Activity.class.getDeclaredClasses(); Class<?> translucentConversionListenerClazz = null; for (Class clazz : classes) { if (clazz.getSimpleName().contains("TranslucentConversionListener")) { translucentConversionListenerClazz = clazz; } } MyInvocationHandler myInvocationHandler = new MyInvocationHandler(listener); Object obj = Proxy.newProxyInstance(Activity.class.getClassLoader(), new Class[] { translucentConversionListenerClazz }, myInvocationHandler); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Method getActivityOptions = Activity.class.getDeclaredMethod("getActivityOptions"); getActivityOptions.setAccessible(true); Object options = getActivityOptions.invoke(activity); Method method = Activity.class.getDeclaredMethod("convertToTranslucent", translucentConversionListenerClazz, ActivityOptions.class); method.setAccessible(true); method.invoke(activity, obj, options); } else { Method method = Activity.class.getDeclaredMethod("convertToTranslucent", translucentConversionListenerClazz); method.setAccessible(true); method.invoke(activity, obj); } mTranslucentState = CHANGE_STATE_SUCCEED; } catch (Throwable t) { mTranslucentState = CHANGE_STATE_FAIL; PreferencesUtils.saveInt(TRANSLUCENT_STATE, CHANGE_STATE_FAIL); new Handler().postDelayed(new Runnable() { @Override public void run() { if (listener != null) { listener.onTranslucent(); } } }, 100); } } }
讓activity變不透明的方法比較簡單;讓activity變透明的方法參數里傳入了一個listener接口 ,主要是當antivity變透明后會回調,因為這個接口也在activity里,而且是私有的,所以我們只能通過動態代理去獲取這個回調。最后如果版本大于等于5.0,還需要再傳入一個ActivityOptions參數。
在實際開發中,這兩個方法在android 5.0以上是有效的,在5.0以下需要當android:windowIsTranslucent為true時才有效,這樣又回到了之前的問題activity切換動畫異常。
**最終決解方法:**setContentView之前就調用 convertActivityFromTranslucent方法,讓activity背景變黑,這樣activity切換效果就正常。
**總結:**在style中設置android:windowIsTranslucent為true ,setContentView之前就調用 convertActivityFromTranslucent方法,當觸發右滑時調用convertActivityToTranslucent,通過動態代理獲取activity變透明后的回調,在回調后允許開始滑動。
二. 讓BaseActivity繼承SwipeBackActivity(SwipeBackActivity講解)
先直接看代碼,比較少
public abstract class SwipeBackActivity extends CoreBaseActivity { /** * 滑動返回View */ private SwipeBackLayout mSwipeBackLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (!isSwipeBackDisableForever()) { TranslucentHelper.convertActivityFromTranslucent(this); mSwipeBackLayout = new SwipeBackLayout(this); } } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); if (!isSwipeBackDisableForever()) { mSwipeBackLayout.attachToActivity(this); mSwipeBackLayout.setOnSwipeBackListener(new SwipeBackLayout.onSwipeBackListener() { @Override public void onStart() { onSwipeBackStart(); } @Override public void onEnd() { onSwipeBackEnd(); } }); } } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (!isSwipeBackDisableForever() && hasFocus) { getSwipeBackLayout().recovery(); } } /** * 滑動返回開始時的回調 */ protected void onSwipeBackStart() {} /** * 滑動返回結束時的回調 */ protected void onSwipeBackEnd() {} /** * 設置是否可以邊緣滑動返回,需要在onCreate方法調用 */ public void setSwipeBackEnable(boolean enable) { if (mSwipeBackLayout != null) { mSwipeBackLayout.setSwipeBackEnable(enable); } } public boolean isSwipeBackDisableForever() { return false; } public SwipeBackLayout getSwipeBackLayout() { return mSwipeBackLayout; } }
SwipeBackActivity中包含了一個SwipeBackLayout對象
在onCreate方法中:
1.activity轉化為不透明。
2.new了一個SwipeBackLayout。
在onPostCreate方法中:
1.attachToActivity主要是插入SwipeBackLayout、窗口背景設置……
2.設置了滑動返回開始和結束的監聽接口,建議在滑動返回開始時,把PopupWindow給dismiss掉。
onWindowFocusChanged 方法中
如果是hasFocus == true,就recovery()這個SwipeBackLayout,這個也是因為下層activity有聯動效果而移動了SwipeBackLayout,所以需要recovery()下,防止異常情況。
isSwipeBackDisableForever 方法是一個大開關,默認返回false,在代碼中復寫后返回 true,則相當于直接繼承了SwipeBackActivity的父類。
setSwipeBackEnable 方法是一個小開關,設置了false之后就暫時不能滑動返回了,可以在特定的時機設置為true,就恢復滑動返回的功能。
**總結說明:**下層activity設置了setSwipeBackEnable 為 false,上層activity滑動時還是可以聯動的,比如MainActivity。而isSwipeBackDisableForever 返回true就不會聯動了,而且一些仿PopupWindow的activity需要復寫這個方法,因為activity需要透明。
三、滑動助手類的使用和滑動返回布局類的實現(SwipeBackLayout講解)
直接貼SwipeBackLayout源碼:
/** * 滑動返回容器類 */ public class SwipeBackLayout extends FrameLayout { /** * 滑動銷毀距離比例界限,滑動部分的比例超過這個就銷毀 */ private static final float DEFAULT_SCROLL_THRESHOLD = 0.5f; /** * 滑動銷毀速度界限,超過這個速度就銷毀 */ private static final float DEFAULT_VELOCITY_THRESHOLD = ScreenUtils.dpToPx(250); /** * 最小滑動速度 */ private static final int MIN_FLING_VELOCITY = ScreenUtils.dpToPx(200); /** * 左邊移動的像素值 */ private int mContentLeft; /** * 左邊移動的像素值 / (ContentView的寬+陰影) */ private float mScrollPercent; /** * (ContentView可見部分+陰影)的比例 (即1 - mScrollPercent) */ private float mContentPercent; /** * 陰影圖 */ private Drawable mShadowDrawable; /** * 陰影圖的寬 */ private int mShadowWidth; /** * 內容view,DecorView的原第一個子view */ private View mContentView; /** * 用于記錄ContentView所在的矩形 */ private Rect mContentViewRect = new Rect(); /** * 設置是否可滑動 */ private boolean mIsSwipeBackEnable = true; /** * 是否正在放置 */ private boolean mIsLayout = true; /** * 判斷背景Activity是否啟動進入動畫 */ private boolean mIsEnterAnimRunning = false; /** * 是否是透明的 */ private boolean mIsActivityTranslucent = false; /** * 進入動畫(只在釋放手指時使用) */ private ObjectAnimator mEnterAnim; /** * 退拽助手類 */ private SwipeBackLayoutDragHelper mViewDragHelper; /** * 執行滑動時的最頂層Activity */ private Activity mTopActivity; /** * 后面的Activity的弱引用 */ private WeakReference<Activity> mBackActivityWeakRf; /** * 監聽滑動開始和結束 */ private onSwipeBackListener mListener; public SwipeBackLayout(Context context) { super(context); init(context); } public SwipeBackLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SwipeBackLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mViewDragHelper = SwipeBackLayoutDragHelper.create(SwipeBackLayout.this, new ViewDragCallback()); mViewDragHelper.setEdgeTrackingEnabled(SwipeBackLayoutDragHelper.EDGE_LEFT); mViewDragHelper.setMinVelocity(MIN_FLING_VELOCITY); mViewDragHelper.setMaxVelocity(MIN_FLING_VELOCITY * 2); try { mShadowDrawable = context.getResources().getDrawable(R.drawable.swipeback_shadow_left); } catch (Exception ignored) { } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { try { if (!mIsSwipeBackEnable) { super.onLayout(changed, left, top, right, bottom); return; } mIsLayout = true; if (mContentView != null) { mContentView.layout(mContentLeft, top, mContentLeft + mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight()); } mIsLayout = false; } catch (Exception e) { super.onLayout(changed, left, top, right, bottom); } } @Override public void requestLayout() { if (!mIsLayout || !mIsSwipeBackEnable) { super.requestLayout(); } } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { try { //繪制陰影 if (mContentPercent > 0 && mShadowDrawable != null && child == mContentView && mViewDragHelper.getViewDragState() != SwipeBackLayoutDragHelper.STATE_IDLE) { child.getHitRect(mContentViewRect); mShadowWidth = mShadowDrawable.getIntrinsicWidth(); mShadowDrawable.setBounds(mContentViewRect.left - mShadowWidth, mContentViewRect.top, mContentViewRect.left, mContentViewRect.bottom); mShadowDrawable.draw(canvas); } return super.drawChild(canvas, child, drawingTime); } catch (Exception e) { return super.drawChild(canvas, child, drawingTime); } } @Override public void computeScroll() { mContentPercent = 1 - mScrollPercent; if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (!mIsSwipeBackEnable) { return false; } try { return mViewDragHelper.shouldInterceptTouchEvent(event); } catch (ArrayIndexOutOfBoundsException e) { return super.onInterceptTouchEvent(event); } } @Override public boolean onTouchEvent(MotionEvent event) { if (!mIsSwipeBackEnable) { return false; } try { mViewDragHelper.processTouchEvent(event); return true; } catch (Exception e) { return super.onTouchEvent(event); } } /** * 將View添加到Activity */ public void attachToActivity(Activity activity) { //插入SwipeBackLayout ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView(); ViewGroup decorChild = (ViewGroup) decor.getChildAt(0); decor.removeView(decorChild); if (getParent() != null) { decor.removeView(this); } decor.addView(this); this.removeAllViews(); this.addView(decorChild); //mContentView為SwipeBackLayout的直接子view,獲取window背景色進行賦值 activity.getWindow().setBackgroundDrawableResource(R.color.transparent_white); TypedArray a = activity.getTheme().obtainStyledAttributes(new int[] { android.R.attr.windowBackground }); mContentView = decorChild; mContentView.setBackgroundResource(a.getResourceId(0, 0)); a.recycle(); //拿到頂層activity和下層activity,做聯動操作 mTopActivity = activity; Activity backActivity = ActivityUtils.getSecondTopActivity(); if (backActivity != null && backActivity instanceof SwipeBackActivity) { if (!((SwipeBackActivity) backActivity).isSwipeBackDisableForever()) { mBackActivityWeakRf = new WeakReference<>(backActivity); } } } /** * 設置是否可以滑動返回 */ public void setSwipeBackEnable(boolean enable) { mIsSwipeBackEnable = enable; } public boolean isActivityTranslucent() { return mIsActivityTranslucent; } /** * 啟動進入動畫 */ private void startEnterAnim() { if (mContentView != null) { ObjectAnimator anim = ObjectAnimator.ofFloat(mContentView, "TranslationX", mContentView.getTranslationX(), 0f); anim.setDuration((long) (125 * mContentPercent)); mEnterAnim = anim; mEnterAnim.start(); } } protected View getContentView() { return mContentView; } private class ViewDragCallback extends SwipeBackLayoutDragHelper.Callback { @Override public boolean tryCaptureView(View child, int pointerId) { if (mIsSwipeBackEnable && mViewDragHelper.isEdgeTouched(SwipeBackLayoutDragHelper.EDGE_LEFT, pointerId)) { TranslucentHelper.convertActivityToTranslucent(mTopActivity, new TranslucentHelper.TranslucentListener() { @Override public void onTranslucent() { if (mListener != null) { mListener.onStart(); } mIsActivityTranslucent = true; } }); return true; } return false; } @Override public int getViewHorizontalDragRange(View child) { return mIsSwipeBackEnable ? SwipeBackLayoutDragHelper.EDGE_LEFT : 0; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); if (changedView == mContentView) { mScrollPercent = Math.abs((float) left / mContentView.getWidth()); mContentLeft = left; //未執行動畫就平移 if (!mIsEnterAnimRunning) { moveBackActivity(); } invalidate(); if (mScrollPercent >= 1 && !mTopActivity.isFinishing()) { if (mBackActivityWeakRf != null && ActivityUtils.activityIsAlive(mBackActivityWeakRf.get())) { ((SwipeBackActivity) mBackActivityWeakRf.get()).getSwipeBackLayout().invalidate(); } mTopActivity.finish(); mTopActivity.overridePendingTransition(0, 0); } } } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { if (xvel > DEFAULT_VELOCITY_THRESHOLD || mScrollPercent > DEFAULT_SCROLL_THRESHOLD) { if (mIsActivityTranslucent) { mViewDragHelper.settleCapturedViewAt(releasedChild.getWidth() + mShadowWidth, 0); if (mContentPercent < 0.85f) { startAnimOfBackActivity(); } } } else { mViewDragHelper.settleCapturedViewAt(0, 0); } if (mListener != null) { mListener.onEnd(); } invalidate(); } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return Math.min(child.getWidth(), Math.max(left, 0)); } @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); if (state == SwipeBackLayoutDragHelper.STATE_IDLE && mScrollPercent < 1f) { TranslucentHelper.convertActivityFromTranslucent(mTopActivity); mIsActivityTranslucent = false; } } @Override public boolean isTranslucent() { return SwipeBackLayout.this.isActivityTranslucent(); } } /** * 背景Activity開始進入動畫 */ private void startAnimOfBackActivity() { if (mBackActivityWeakRf != null && ActivityUtils.activityIsAlive(mBackActivityWeakRf.get())) { mIsEnterAnimRunning = true; SwipeBackLayout swipeBackLayout = ((SwipeBackActivity) mBackActivityWeakRf.get()).getSwipeBackLayout(); swipeBackLayout.startEnterAnim(); } } /** * 移動背景Activity */ private void moveBackActivity() { if (mBackActivityWeakRf != null && ActivityUtils.activityIsAlive(mBackActivityWeakRf.get())) { View view = ((SwipeBackActivity) mBackActivityWeakRf.get()).getSwipeBackLayout().getContentView(); if (view != null) { int width = view.getWidth(); view.setTranslationX(-width * 0.3f * Math.max(0f, mContentPercent - 0.15f)); } } } /** * 回復界面的平移到初始位置 */ public void recovery() { if (mEnterAnim != null && mEnterAnim.isRunning()) { mEnterAnim.end(); } else { mContentView.setTranslationX(0); } } interface onSwipeBackListener { void onStart(); void onEnd(); } public void setOnSwipeBackListener(onSwipeBackListener listener) { mListener = listener; } }
attachToActivity
上面講到SwipeBackLayout是在activity的onCreate時被創建,在onPostCreate是插入到DecorView里,主要是因為DecorView是在setContentView時與Window關聯起來插入SwipeBackLayout方法如代碼所示,不難,然后是設置了window的背景色為透明色,mContentView為SwipeBackLayout的直接子view,獲取window背景色進行賦值。由于考拉項目已經有很多activity,而這些activity中android:windowBackground設置的顏色大部分是白色,少部分是灰色和透明的,所以需要在代碼中設置統一設置一遍透明的,原來的背景色則賦值給SwipeBackLayout的子View就可以達到最少修改代碼的目的。最后拿到頂層activity和下層activity,做聯動操作
SwipeBackLayoutDragHelper 和 ViewDragCallback
SwipeBackLayout中包含了一個滑動助手類(SwipeBackLayoutDragHelper)的對象,該類是在ViewDragHelper的基礎上進行修改得來的。
修改點:
1.右側觸發區域 EDGE_SIZE由 20dp 改到 25dp
2.提供滑動最大速度 的設置方法
3.在ViewDragHelper 的內部類Callback方法中提供是否activity為透明的回調接口
4.在最終調用滑動的方法dragTo中添加判斷邏輯,activity為透明時才支持滑動
SwipeBackLayoutDragHelper 在init 方法中初始化,通過onInterceptTouchEvent和onTouchEvent拿到滑動事件,通過ViewDragCallback的一些方法返回相應的滑動回調,ViewDragCallback實現了SwipeBackLayoutDragHelper.Callback里的以下幾個接口,其中其中isTranslucent()是自己添加進去的。
tryCaptureView方法當觸摸到SwipeBackLayout里的子View時觸發的,當返回true,表示捕捉成功,否則失敗。判斷條件是如果支持滑動返回并且是左側邊距被觸摸時才可以,我們知道這個時候的的背景色是不透明的,如果直接開始滑動則是黑色的,所以需要在這里背景色改成透明的,如果直接調用 TranslucentHelper.convertActivityToTranslucent(mTopActivity, null)后直接返回true,會出現一個異常情況,就是滑動過快時會導致背景還來不及變成黑色就滑動出來了,之后才變成透明的,從而導致了會從黑色到透明的一個閃爍現象,解決的辦法是在代碼中用了一個回調和標記,當變成透明后設置了mIsActivityTranslucent = true;通過mIsActivityTranslucent 這個變量來判斷是否進行移動的操作。由于修改activity變透明的方法是通過反射的,不能簡單的設置一個接口后進行回調,而是通過動態代理的方式來實現的(InvocationHandler),在convertToTranslucent方法的第一個參數剛好是一個判斷activity是否已經變成透明的回調,看下面代碼中 if 語句里的注釋和回調,如果窗口已經變成透明的話,就傳了一個drawComplete (true)。通過動態代理,將translucentConversionListenerClazz 執行其方法onTranslucentConversionComplete的替換成myInvocationHandler中執行invoke方法。其中賦值給success的args[0]正是 drawComplete。
isTranslucent是自己添加了一個方法,主要是返回activity是否是透明的默認為true,在SwipeBackLayout重寫后將mIsActivityTranslucent返回。仔細看SwipeBackLayoutDragHelper方法的話,會發現最后通過dragTo方法對view進行移動,因此在進行水平移動前判斷下是否是透明的,只有透明了才能移動
onViewPositionChanged view移動過程中會持續調用,這里面的邏輯主要有這幾個:
1.實時計算滑動了多少距離,用于繪制左側陰影等
2.使下面的activity進行移動moveBackActivity();
3.當view完全移出屏幕后,銷毀當前的activity
onViewReleased是手指釋放后觸發的一個方法。如果滑動速度大于最大速度或者滑動的距離大于設定的閾值距離,則直接移到屏幕外,同時觸發下層activity的復位動畫,否則移會到原來位置 。
onViewDragStateChanged當滑動的狀態發生改變時的回調,主要是停止滑動后,將背景改成不透明,這樣跳到別的頁面是動畫就是正常的。
clampViewPositionHorizontal 返回水平移動距離,防止滑出父 view。
getViewHorizontalDragRange對于clickable=true的子view,需要返回大于0的數字才能正常捕獲。
其他方法都較為簡單,注釋也寫了,就不多說了,最后毫不吝嗇的貼上SwipeBackLayoutDragHelper的dragTo代碼,就多了if (mCallback.isTranslucent())
private void dragTo(int left, int top, int dx, int dy) { int clampedX = left; int clampedY = top; final int oldLeft = mCapturedView.getLeft(); final int oldTop = mCapturedView.getTop(); if (dx != 0) { clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx); if (mCallback.isTranslucent()) { ViewCompat.offsetLeftAndRight(mCapturedView, clampedX - oldLeft); } } if (dy != 0) { clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy); ViewCompat.offsetTopAndBottom(mCapturedView, clampedY - oldTop); } if (dx != 0 || dy != 0) { final int clampedDx = clampedX - oldLeft; final int clampedDy = clampedY - oldTop; if (mCallback.isTranslucent()) { mCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY, clampedDx, clampedDy); } } }
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。