国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

Android仿考拉全局滑動(dòng)返回及聯(lián)動(dòng)效果的實(shí)現(xiàn)方法

2019-10-21 21:41:48
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

首次通過(guò)右滑來(lái)返回到上一個(gè)頁(yè)面的操作是在 IOS7上出現(xiàn)。到目前android應(yīng)用上支持這種操作的依然不多。分析其主要原因應(yīng)該是android已有實(shí)體的返回按鍵,這樣的功能變得不重要,但我覺(jué)得有這樣的功能便于單手操作,能提升app的用戶體驗(yàn),特別是從ios轉(zhuǎn)到android的用戶。寫這篇博文希望可以對(duì)大家有所幫助,希望自己的app上有滑動(dòng)返回功能的可以參考下。

原理的簡(jiǎn)單描述

Android系統(tǒng)里有很多滑動(dòng)相關(guān)的API和類,比如ViewDragHelper就是一個(gè)很好的滑動(dòng)助手類。首先設(shè)置Window的背景為透明,再通過(guò)ViewDragHelper對(duì)Activity上DecorView的子view進(jìn)行滑動(dòng),當(dāng)滑動(dòng)到一定距離,手指離開(kāi)后就自動(dòng)滑到最右側(cè),然后finish當(dāng)前的activity,這樣即可實(shí)現(xiàn)滑動(dòng)返回效果。為了能夠 “全局的”、“聯(lián)動(dòng)的” 實(shí)現(xiàn)滑動(dòng)返回效果,在每個(gè)activity的DecorView下插入了SwipeBackLayout,當(dāng)前activity滑動(dòng)和下層activity的聯(lián)動(dòng)都在該類中完成。

效果圖

Android,全局滑動(dòng),聯(lián)動(dòng)

布局圖

Android,全局滑動(dòng),聯(lián)動(dòng)

實(shí)現(xiàn)主要類:

SwipeBackActivity //滑動(dòng)返回基類

SwipeBackLayout //滑動(dòng)返回布局類

SwipeBackLayoutDragHelper  //修改ViewDragHelper后助手類

TranslucentHelper //代碼中修改透明或者不透明的助手類

##代碼層面的講解

一. 設(shè)置activity為透明、activity跳轉(zhuǎn)動(dòng)畫(TranslucentHelper 講解)

這個(gè)看起來(lái)很簡(jiǎn)單,但如果要兼容到API16及以下,會(huì)遇到過(guò)一個(gè)比較麻煩的頁(yè)面切換動(dòng)畫問(wèn)題:

1.1、通過(guò)activity的主題style進(jìn)行設(shè)置

<item name="android:windowBackground">@color/transparent</item><item name="android:windowIsTranslucent">true</item>```**遇到問(wèn)題:**如果在某個(gè)activity的主題style中設(shè)置了android:windowIsTranslucent屬性為true,那么該activity切換動(dòng)畫與沒(méi)設(shè)置之前是不同的,有些手機(jī)切換動(dòng)畫會(huì)變得非常跳。所以需要自定義activity的切換動(dòng)畫。接下來(lái)我們會(huì)想到通過(guò)主題style里的windowAnimationStyle來(lái)設(shè)置切換動(dòng)畫

@anim/activity_open_enter

@anim/activity_open_exit

@anim/activity_close_enter

@anim/activity_close_exit```

**實(shí)踐證明:**當(dāng)android:windowIsTranslucent為true時(shí),以上幾個(gè)屬性是無(wú)效的,而下面兩個(gè)屬性還是可以用。但是這兩個(gè)屬性一個(gè)是窗口進(jìn)來(lái)動(dòng)畫,一個(gè)是窗口退出動(dòng)畫,明顯是不夠。

<item name="android:windowEnterAnimation">@anim/***</item><item name="android:windowExitAnimation">@anim/***</item>``

結(jié)合overridePendingTransition(int enterAnim, int exitAnim)可以復(fù)寫窗口進(jìn)來(lái)動(dòng)畫和窗口退出動(dòng)畫,這種我覺(jué)得最終可能是可以實(shí)現(xiàn)的,不過(guò)控制起來(lái)比較復(fù)雜:

比如有A、B、C三個(gè)頁(yè)面:

A跳到B,進(jìn)場(chǎng)頁(yè)面B動(dòng)畫從右進(jìn)來(lái),出場(chǎng)頁(yè)面A動(dòng)畫從左出去,可以直接在style中寫死

@anim/***

@anim/***```

如果B返回到A,進(jìn)場(chǎng)頁(yè)面A動(dòng)畫從左進(jìn)來(lái),出場(chǎng)頁(yè)面B動(dòng)畫從右出去,此時(shí)需要通過(guò)復(fù)寫onBackPressed() 方法,
在其中添加overridePendingTransition(int enterAnim, int exitAnim)方法來(lái)改變動(dòng)畫。

如果B是finish()后到A頁(yè)面,在finish()后面加上overridePendingTransition ……

由于onBackPressed() 方法最終會(huì)調(diào)finish(),所以實(shí)際上只需要復(fù)寫finish(),在其中添加overridePendingTransition……

但是假如B finish()后跳到C,則又不應(yīng)該執(zhí)行overridePendingTransition……,那么就需要判斷finish執(zhí)行后是否要加 overridePendingTransition……

對(duì)于一個(gè)較為龐大的項(xiàng)目,采取這種方法需要對(duì)每個(gè)頁(yè)面進(jìn)行排查,因此是不可行的,而對(duì)于剛剛起步的應(yīng)用來(lái)說(shuō)則是一個(gè)選擇。

1.2、通過(guò)透明助手類(TranslucentHelper)進(jìn)行設(shè)置

透明助手類(TranslucentHelper)里主要又有兩個(gè)方法,一個(gè)是讓activity變不透明,一個(gè)是讓activity變透明,這兩個(gè)都是通過(guò)反射來(lái)調(diào)用隱藏的系統(tǒng)api來(lái)實(shí)現(xiàn)的。因?yàn)檩^低的版本不支持代碼中修改背景透明不透明,所以在類中有個(gè)靜態(tài)變量mTranslucentState 來(lái)記錄是否可以切換背景,這樣低版本就不需要每次都反射通過(guò)捕獲到的異常來(lái)做兼容方案。

另外:發(fā)現(xiàn)有些手機(jī)支持背景變黑,但不支持背景變透明(中興z9 mini 5.0.2系統(tǒng))

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;//表示確認(rèn)不可以切換透明狀態(tài) private static final int CHANGE_STATE_SUCCEED = CHANGE_STATE_FAIL + 1;//表示確認(rèn)可以切換透明狀態(tài) 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變不透明的方法比較簡(jiǎn)單;讓activity變透明的方法參數(shù)里傳入了一個(gè)listener接口 ,主要是當(dāng)antivity變透明后會(huì)回調(diào),因?yàn)檫@個(gè)接口也在activity里,而且是私有的,所以我們只能通過(guò)動(dòng)態(tài)代理去獲取這個(gè)回調(diào)。最后如果版本大于等于5.0,還需要再傳入一個(gè)ActivityOptions參數(shù)。

在實(shí)際開(kāi)發(fā)中,這兩個(gè)方法在android 5.0以上是有效的,在5.0以下需要當(dāng)android:windowIsTranslucent為true時(shí)才有效,這樣又回到了之前的問(wèn)題activity切換動(dòng)畫異常。

**最終決解方法:**setContentView之前就調(diào)用 convertActivityFromTranslucent方法,讓activity背景變黑,這樣activity切換效果就正常。

**總結(jié):**在style中設(shè)置android:windowIsTranslucent為true ,setContentView之前就調(diào)用 convertActivityFromTranslucent方法,當(dāng)觸發(fā)右滑時(shí)調(diào)用convertActivityToTranslucent,通過(guò)動(dòng)態(tài)代理獲取activity變透明后的回調(diào),在回調(diào)后允許開(kāi)始滑動(dòng)。

二. 讓BaseActivity繼承SwipeBackActivity(SwipeBackActivity講解)

先直接看代碼,比較少

public abstract class SwipeBackActivity extends CoreBaseActivity { /**  * 滑動(dòng)返回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();  } } /**  * 滑動(dòng)返回開(kāi)始時(shí)的回調(diào)  */ protected void onSwipeBackStart() {} /**  * 滑動(dòng)返回結(jié)束時(shí)的回調(diào)  */ protected void onSwipeBackEnd() {} /**  * 設(shè)置是否可以邊緣滑動(dòng)返回,需要在onCreate方法調(diào)用  */ public void setSwipeBackEnable(boolean enable) {  if (mSwipeBackLayout != null) {   mSwipeBackLayout.setSwipeBackEnable(enable);  } } public boolean isSwipeBackDisableForever() {  return false; } public SwipeBackLayout getSwipeBackLayout() {  return mSwipeBackLayout; }}

SwipeBackActivity中包含了一個(gè)SwipeBackLayout對(duì)象

在onCreate方法中:

1.activity轉(zhuǎn)化為不透明。

2.new了一個(gè)SwipeBackLayout。

在onPostCreate方法中:

1.attachToActivity主要是插入SwipeBackLayout、窗口背景設(shè)置……

2.設(shè)置了滑動(dòng)返回開(kāi)始和結(jié)束的監(jiān)聽(tīng)接口,建議在滑動(dòng)返回開(kāi)始時(shí),把PopupWindow給dismiss掉。

onWindowFocusChanged 方法中

如果是hasFocus == true,就recovery()這個(gè)SwipeBackLayout,這個(gè)也是因?yàn)橄聦觓ctivity有聯(lián)動(dòng)效果而移動(dòng)了SwipeBackLayout,所以需要recovery()下,防止異常情況。

isSwipeBackDisableForever 方法是一個(gè)大開(kāi)關(guān),默認(rèn)返回false,在代碼中復(fù)寫后返回 true,則相當(dāng)于直接繼承了SwipeBackActivity的父類。

setSwipeBackEnable 方法是一個(gè)小開(kāi)關(guān),設(shè)置了false之后就暫時(shí)不能滑動(dòng)返回了,可以在特定的時(shí)機(jī)設(shè)置為true,就恢復(fù)滑動(dòng)返回的功能。

**總結(jié)說(shuō)明:**下層activity設(shè)置了setSwipeBackEnable 為 false,上層activity滑動(dòng)時(shí)還是可以聯(lián)動(dòng)的,比如MainActivity。而isSwipeBackDisableForever 返回true就不會(huì)聯(lián)動(dòng)了,而且一些仿PopupWindow的activity需要復(fù)寫這個(gè)方法,因?yàn)閍ctivity需要透明。

三、滑動(dòng)助手類的使用和滑動(dòng)返回布局類的實(shí)現(xiàn)(SwipeBackLayout講解)

直接貼SwipeBackLayout源碼:

/** * 滑動(dòng)返回容器類 */public class SwipeBackLayout extends FrameLayout { /**  * 滑動(dòng)銷毀距離比例界限,滑動(dòng)部分的比例超過(guò)這個(gè)就銷毀  */ private static final float DEFAULT_SCROLL_THRESHOLD = 0.5f; /**  * 滑動(dòng)銷毀速度界限,超過(guò)這個(gè)速度就銷毀  */ private static final float DEFAULT_VELOCITY_THRESHOLD = ScreenUtils.dpToPx(250); /**  * 最小滑動(dòng)速度  */ private static final int MIN_FLING_VELOCITY = ScreenUtils.dpToPx(200); /**  * 左邊移動(dòng)的像素值  */ private int mContentLeft; /**  * 左邊移動(dòng)的像素值 / (ContentView的寬+陰影)  */ private float mScrollPercent; /**  * (ContentView可見(jiàn)部分+陰影)的比例 (即1 - mScrollPercent)  */ private float mContentPercent; /**  * 陰影圖  */ private Drawable mShadowDrawable; /**  * 陰影圖的寬  */ private int mShadowWidth; /**  * 內(nèi)容view,DecorView的原第一個(gè)子view  */ private View mContentView; /**  * 用于記錄ContentView所在的矩形  */ private Rect mContentViewRect = new Rect(); /**  * 設(shè)置是否可滑動(dòng)  */ private boolean mIsSwipeBackEnable = true; /**  * 是否正在放置  */ private boolean mIsLayout = true; /**  * 判斷背景Activity是否啟動(dòng)進(jìn)入動(dòng)畫  */ private boolean mIsEnterAnimRunning = false; /**  * 是否是透明的  */ private boolean mIsActivityTranslucent = false; /**  * 進(jìn)入動(dòng)畫(只在釋放手指時(shí)使用)  */ private ObjectAnimator mEnterAnim; /**  * 退拽助手類  */ private SwipeBackLayoutDragHelper mViewDragHelper; /**  * 執(zhí)行滑動(dòng)時(shí)的最頂層Activity  */ private Activity mTopActivity; /**  * 后面的Activity的弱引用  */ private WeakReference<Activity> mBackActivityWeakRf; /**  * 監(jiān)聽(tīng)滑動(dòng)開(kāi)始和結(jié)束  */ 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背景色進(jìn)行賦值  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,做聯(lián)動(dòng)操作  mTopActivity = activity;  Activity backActivity = ActivityUtils.getSecondTopActivity();  if (backActivity != null && backActivity instanceof SwipeBackActivity) {   if (!((SwipeBackActivity) backActivity).isSwipeBackDisableForever()) {    mBackActivityWeakRf = new WeakReference<>(backActivity);   }  } } /**  * 設(shè)置是否可以滑動(dòng)返回  */ public void setSwipeBackEnable(boolean enable) {  mIsSwipeBackEnable = enable; } public boolean isActivityTranslucent() {  return mIsActivityTranslucent; } /**  * 啟動(dòng)進(jìn)入動(dòng)畫  */ 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;    //未執(zhí)行動(dòng)畫就平移    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開(kāi)始進(jìn)入動(dòng)畫  */ private void startAnimOfBackActivity() {  if (mBackActivityWeakRf != null && ActivityUtils.activityIsAlive(mBackActivityWeakRf.get())) {   mIsEnterAnimRunning = true;   SwipeBackLayout swipeBackLayout = ((SwipeBackActivity) mBackActivityWeakRf.get()).getSwipeBackLayout();   swipeBackLayout.startEnterAnim();  } } /**  * 移動(dòng)背景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));   }  } } /**  * 回復(fù)界面的平移到初始位置  */ 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時(shí)被創(chuàng)建,在onPostCreate是插入到DecorView里,主要是因?yàn)镈ecorView是在setContentView時(shí)與Window關(guān)聯(lián)起來(lái)插入SwipeBackLayout方法如代碼所示,不難,然后是設(shè)置了window的背景色為透明色,mContentView為SwipeBackLayout的直接子view,獲取window背景色進(jìn)行賦值。由于考拉項(xiàng)目已經(jīng)有很多activity,而這些activity中android:windowBackground設(shè)置的顏色大部分是白色,少部分是灰色和透明的,所以需要在代碼中設(shè)置統(tǒng)一設(shè)置一遍透明的,原來(lái)的背景色則賦值給SwipeBackLayout的子View就可以達(dá)到最少修改代碼的目的。最后拿到頂層activity和下層activity,做聯(lián)動(dòng)操作

SwipeBackLayoutDragHelper 和 ViewDragCallback 

SwipeBackLayout中包含了一個(gè)滑動(dòng)助手類(SwipeBackLayoutDragHelper)的對(duì)象,該類是在ViewDragHelper的基礎(chǔ)上進(jìn)行修改得來(lái)的。

修改點(diǎn):

1.右側(cè)觸發(fā)區(qū)域 EDGE_SIZE由 20dp 改到 25dp

2.提供滑動(dòng)最大速度 的設(shè)置方法

3.在ViewDragHelper 的內(nèi)部類Callback方法中提供是否activity為透明的回調(diào)接口

4.在最終調(diào)用滑動(dòng)的方法dragTo中添加判斷邏輯,activity為透明時(shí)才支持滑動(dòng)

SwipeBackLayoutDragHelper 在init 方法中初始化,通過(guò)onInterceptTouchEvent和onTouchEvent拿到滑動(dòng)事件,通過(guò)ViewDragCallback的一些方法返回相應(yīng)的滑動(dòng)回調(diào),ViewDragCallback實(shí)現(xiàn)了SwipeBackLayoutDragHelper.Callback里的以下幾個(gè)接口,其中其中isTranslucent()是自己添加進(jìn)去的。

Android,全局滑動(dòng),聯(lián)動(dòng)

tryCaptureView方法當(dāng)觸摸到SwipeBackLayout里的子View時(shí)觸發(fā)的,當(dāng)返回true,表示捕捉成功,否則失敗。判斷條件是如果支持滑動(dòng)返回并且是左側(cè)邊距被觸摸時(shí)才可以,我們知道這個(gè)時(shí)候的的背景色是不透明的,如果直接開(kāi)始滑動(dòng)則是黑色的,所以需要在這里背景色改成透明的,如果直接調(diào)用 TranslucentHelper.convertActivityToTranslucent(mTopActivity, null)后直接返回true,會(huì)出現(xiàn)一個(gè)異常情況,就是滑動(dòng)過(guò)快時(shí)會(huì)導(dǎo)致背景還來(lái)不及變成黑色就滑動(dòng)出來(lái)了,之后才變成透明的,從而導(dǎo)致了會(huì)從黑色到透明的一個(gè)閃爍現(xiàn)象,解決的辦法是在代碼中用了一個(gè)回調(diào)和標(biāo)記,當(dāng)變成透明后設(shè)置了mIsActivityTranslucent = true;通過(guò)mIsActivityTranslucent 這個(gè)變量來(lái)判斷是否進(jìn)行移動(dòng)的操作。由于修改activity變透明的方法是通過(guò)反射的,不能簡(jiǎn)單的設(shè)置一個(gè)接口后進(jìn)行回調(diào),而是通過(guò)動(dòng)態(tài)代理的方式來(lái)實(shí)現(xiàn)的(InvocationHandler),在convertToTranslucent方法的第一個(gè)參數(shù)剛好是一個(gè)判斷activity是否已經(jīng)變成透明的回調(diào),看下面代碼中 if 語(yǔ)句里的注釋和回調(diào),如果窗口已經(jīng)變成透明的話,就傳了一個(gè)drawComplete (true)。通過(guò)動(dòng)態(tài)代理,將translucentConversionListenerClazz 執(zhí)行其方法onTranslucentConversionComplete的替換成myInvocationHandler中執(zhí)行invoke方法。其中賦值給success的args[0]正是 drawComplete。

isTranslucent是自己添加了一個(gè)方法,主要是返回activity是否是透明的默認(rèn)為true,在SwipeBackLayout重寫后將mIsActivityTranslucent返回。仔細(xì)看SwipeBackLayoutDragHelper方法的話,會(huì)發(fā)現(xiàn)最后通過(guò)dragTo方法對(duì)view進(jìn)行移動(dòng),因此在進(jìn)行水平移動(dòng)前判斷下是否是透明的,只有透明了才能移動(dòng)

onViewPositionChanged view移動(dòng)過(guò)程中會(huì)持續(xù)調(diào)用,這里面的邏輯主要有這幾個(gè):

1.實(shí)時(shí)計(jì)算滑動(dòng)了多少距離,用于繪制左側(cè)陰影等

2.使下面的activity進(jìn)行移動(dòng)moveBackActivity();

3.當(dāng)view完全移出屏幕后,銷毀當(dāng)前的activity

onViewReleased是手指釋放后觸發(fā)的一個(gè)方法。如果滑動(dòng)速度大于最大速度或者滑動(dòng)的距離大于設(shè)定的閾值距離,則直接移到屏幕外,同時(shí)觸發(fā)下層activity的復(fù)位動(dòng)畫,否則移會(huì)到原來(lái)位置 。

onViewDragStateChanged當(dāng)滑動(dòng)的狀態(tài)發(fā)生改變時(shí)的回調(diào),主要是停止滑動(dòng)后,將背景改成不透明,這樣跳到別的頁(yè)面是動(dòng)畫就是正常的。

clampViewPositionHorizontal 返回水平移動(dòng)距離,防止滑出父 view。

getViewHorizontalDragRange對(duì)于clickable=true的子view,需要返回大于0的數(shù)字才能正常捕獲。

其他方法都較為簡(jiǎn)單,注釋也寫了,就不多說(shuō)了,最后毫不吝嗇的貼上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);      }    }  }

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)VEVB武林網(wǎng)的支持。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到Android開(kāi)發(fā)頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 屯留县| 芜湖县| 邹平县| 桂东县| 大同县| 林周县| 德格县| 甘德县| 汉阴县| 成都市| 自贡市| 桓仁| 自治县| 蓝田县| 大余县| 泽州县| 象州县| 尼木县| 兰坪| 眉山市| 鄂尔多斯市| 莫力| 湟源县| 康乐县| 彭阳县| 杨浦区| 怀化市| 龙胜| 盘山县| 修武县| 武汉市| 腾冲县| 类乌齐县| 锡林浩特市| 永顺县| 忻州市| 吉水县| 溆浦县| 象州县| 凌云县| 洛川县|