Android SwipeBackLayout源码解析

pciz9843 8年前
   <p> </p>    <p>Github: <a href="/misc/goto?guid=4958988897536054229" rel="nofollow,noindex">SwipeBackLayout</a> 分析版本: <a href="/misc/goto?guid=4959672703905383763" rel="nofollow,noindex">e4ddae6</a></p>    <p>SwipeBackLayout 是一个仿 IOS 通过手势退出界面的开源库。</p>    <h2>SwipeBackLayout</h2>    <p><img src="https://simg.open-open.com/show/981e05df3d0c85e4e6ca8bf51bbdd980.png"></p>    <p>SwipeBackLayout 可以通过在左、右和下边缘来拖动整个 Activity 达到退出 Activity 的效果。</p>    <h2>使用</h2>    <p>添加到 Gradle :</p>    <pre>  <code class="language-java">compile 'me.imid.swipebacklayout.lib:library:1.0.0'  </code></pre>    <p>继承 SwipeBackActivity :</p>    <pre>  <code class="language-java">public class DemoActivity extends SwipeBackActivity {  }  </code></pre>    <ul>     <li>onCreate 中 setContentView() 照常使用</li>     <li>可以通过 getSwipeBackLayout() 定制 SwipeBackLayout</li>    </ul>    <p>在 styles.xml 中的主题中添加:</p>    <pre>  <code class="language-java"><item name="android:windowIsTranslucent">true</item>  </code></pre>    <h3>注意</h3>    <p>需要在项目中添加最新的 supportV4 包</p>    <h3>demo</h3>    <pre>  <code class="language-java">public class DemoActivity extends SwipeBackActivity implements View.OnClickListener {      private int[] mBgColors;        private static int mBgIndex = 0;        private String mKeyTrackingMode;        private RadioGroup mTrackingModeGroup;        private SwipeBackLayout mSwipeBackLayout;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_demo);          changeActionBarColor();          findViews();          mKeyTrackingMode = getString(R.string.key_tracking_mode);          mSwipeBackLayout = getSwipeBackLayout();            mTrackingModeGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {              @Override              public void onCheckedChanged(RadioGroup group, int checkedId) {                  int edgeFlag;                  switch (checkedId) {                      case R.id.mode_left:                          edgeFlag = SwipeBackLayout.EDGE_LEFT;                          break;                      case R.id.mode_right:                          edgeFlag = SwipeBackLayout.EDGE_RIGHT;                          break;                      case R.id.mode_bottom:                          edgeFlag = SwipeBackLayout.EDGE_BOTTOM;                          break;                      default:                          edgeFlag = SwipeBackLayout.EDGE_ALL;                  }                  mSwipeBackLayout.setEdgeTrackingEnabled(edgeFlag);                  saveTrackingMode(edgeFlag);              }          });      }  ...  </code></pre>    <h2>源码</h2>    <h3>SwipeBackActivity</h3>    <pre>  <code class="language-java">public class SwipeBackActivity extends AppCompatActivity implements SwipeBackActivityBase {      private SwipeBackActivityHelper mHelper;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          mHelper = new SwipeBackActivityHelper(this);          mHelper.onActivityCreate();      }        @Override      protected void onPostCreate(Bundle savedInstanceState) {          super.onPostCreate(savedInstanceState);          mHelper.onPostCreate();      }        @Override      public View findViewById(int id) {          View v = super.findViewById(id);          if (v == null && mHelper != null)              return mHelper.findViewById(id);          return v;      }        @Override      public SwipeBackLayout getSwipeBackLayout() {//SwipeBackActivityBase接口中的方法          return mHelper.getSwipeBackLayout();      }        @Override      public void setSwipeBackEnable(boolean enable) {//SwipeBackActivityBase接口中的方法          getSwipeBackLayout().setEnableGesture(enable);      }        @Override      public void scrollToFinishActivity() {//SwipeBackActivityBase接口中的方法          Utils.convertActivityToTranslucent(this);          getSwipeBackLayout().scrollToFinishActivity();      }  }  </code></pre>    <p>在 SwipeBackActivity 中实现了 SwipeBackActivityBase 接口,在 Activity 的生命周期函数 onCreate() 中创建了 SwipeBackActivityHelper 对象, 该类的作用是设置 Activity 的透明和在 DecorView 中替换 SwipeBackLayout 。 onPostCreate() 是在 Activity 完全运行起来之后才会被调用。其中 findViewById() 方法进行了判断,首先在 Activity 的 contentView 中获取,获取不到再到 SwipeBackLayout 中获取。</p>    <h3>SwipeBackActivityHelper</h3>    <p>在 SwipeBackActivity 的 onCreate() 中的调用方法:</p>    <pre>  <code class="language-java">public class SwipeBackActivityHelper {      private SwipeBackLayout mSwipeBackLayout;        public SwipeBackActivityHelper(Activity activity) {          mActivity = activity;      }        @SuppressWarnings("deprecation")      public void onActivityCreate() {          //设置Window的background为透明          mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));          //设置decorView没有background          mActivity.getWindow().getDecorView().setBackgroundDrawable(null);          //inflate一个SwipeBackLayout出来          mSwipeBackLayout = (SwipeBackLayout) LayoutInflater.from(mActivity).inflate(                  me.imid.swipebacklayout.lib.R.layout.swipeback_layout, null);          //设置手势滑动监听器          mSwipeBackLayout.addSwipeListener(new SwipeBackLayout.SwipeListener() {              @Override              public void onScrollStateChange(int state, float scrollPercent) {              }                @Override              public void onEdgeTouch(int edgeFlag) {                  //当有边界触摸的时候设置成透明的                  Utils.convertActivityToTranslucent(mActivity);              }                @Override              public void onScrollOverThreshold() {                }          });      }  }  </code></pre>    <p>在 onActivityCreate 中主要就是将 window 、 decorView 的背景设置为透明的。</p>    <p>在 SwipeBackActivity 的 onPostCreate() 中的调用方法:</p>    <pre>  <code class="language-java">public class SwipeBackActivityHelper {   public void onPostCreate() {          mSwipeBackLayout.attachToActivity(mActivity);      }  }  </code></pre>    <p>在 attachToActivity 中的操作就是将 decorView 中的 childView 换成 SwipeBackLayout ,然后将 childView 添加到 SwipeBackLayout 中。</p>    <p>其他的方法:</p>    <pre>  <code class="language-java">public class SwipeBackActivityHelper {      public View findViewById(int id) {          if (mSwipeBackLayout != null) {              return mSwipeBackLayout.findViewById(id);          }          return null;      }        public SwipeBackLayout getSwipeBackLayout() {          return mSwipeBackLayout;      }  }  </code></pre>    <h3>SwipeBackLayout</h3>    <p>SwipeBackLayout 是一个 View ,可以从构造函数开始看:</p>    <pre>  <code class="language-java">public class SwipeBackLayout extends FrameLayout {       /**   * Minimum velocity that will be detected as a fling   */      private static final int MIN_FLING_VELOCITY = 400; // dips per second          private static final int[] EDGE_FLAGS = {              EDGE_LEFT, EDGE_RIGHT, EDGE_BOTTOM, EDGE_ALL      };   private int mEdgeFlag;       private ViewDragHelper mDragHelper;     public SwipeBackLayout(Context context) {          this(context, null);      }        public SwipeBackLayout(Context context, AttributeSet attrs) {          this(context, attrs, R.attr.SwipeBackLayoutStyle);      }        public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) {          super(context, attrs);          mDragHelper = ViewDragHelper.create(this, new ViewDragCallback());            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeBackLayout, defStyle,                  R.style.SwipeBackLayout);            //与边缘可拖动的距离          int edgeSize = a.getDimensionPixelSize(R.styleable.SwipeBackLayout_edge_size, -1);          if (edgeSize > 0) {              //设置给ViewDragHelper              setEdgeSize(edgeSize);          }          //边缘模式,分为EDGE_LEFT, EDGE_RIGHT, EDGE_BOTTOM, EDGE_ALL          int mode = EDGE_FLAGS[a.getInt(R.styleable.SwipeBackLayout_edge_flag, 0)];          //设置给ViewDragHelper          setEdgeTrackingEnabled(mode);          //边缘滑动的时候的阴影          int shadowLeft = a.getResourceId(R.styleable.SwipeBackLayout_shadow_left,                  R.drawable.shadow_left);          int shadowRight = a.getResourceId(R.styleable.SwipeBackLayout_shadow_right,                  R.drawable.shadow_right);          int shadowBottom = a.getResourceId(R.styleable.SwipeBackLayout_shadow_bottom,                  R.drawable.shadow_bottom);          setShadow(shadowLeft, EDGE_LEFT);          setShadow(shadowRight, EDGE_RIGHT);          setShadow(shadowBottom, EDGE_BOTTOM);          a.recycle();          //得到密度          final float density = getResources().getDisplayMetrics().density;          //手势滑动最小速度          final float minVel = MIN_FLING_VELOCITY * density;          //设置给ViewDragHelper          mDragHelper.setMinVelocity(minVel);          mDragHelper.setMaxVelocity(minVel * 2f);      }          /**   * Set the size of an edge. This is the range in pixels along the edges of   * this view that will actively detect edge touches or drags if edge   * tracking is enabled.   *   * @param size The size of an edge in pixels   */      public void setEdgeSize(int size) {          mDragHelper.setEdgeSize(size);      }          /**   * Enable edge tracking for the selected edges of the parent view. The   * callback's   * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeTouched(int, int)}   * and   * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeDragStarted(int, int)}   * methods will only be invoked for edges for which edge tracking has been   * enabled.   *   * @param edgeFlags Combination of edge flags describing the edges to watch   * @see #EDGE_LEFT   * @see #EDGE_RIGHT   * @see #EDGE_BOTTOM   */      public void setEdgeTrackingEnabled(int edgeFlags) {          mEdgeFlag = edgeFlags;          mDragHelper.setEdgeTrackingEnabled(mEdgeFlag);      }       public void setShadow(int resId, int edgeFlag) {          setShadow(getResources().getDrawable(resId), edgeFlag);      }       /**   * Set a drawable used for edge shadow.   *   * @param shadow Drawable to use   * @param edgeFlag Combination of edge flags describing the edge to set   * @see #EDGE_LEFT   * @see #EDGE_RIGHT   * @see #EDGE_BOTTOM   */      public void setShadow(Drawable shadow, int edgeFlag) {          if ((edgeFlag & EDGE_LEFT) != 0) {              mShadowLeft = shadow;          } else if ((edgeFlag & EDGE_RIGHT) != 0) {              mShadowRight = shadow;          } else if ((edgeFlag & EDGE_BOTTOM) != 0) {              mShadowBottom = shadow;          }          invalidate();      }         //处理ViewDragHelper     @Override      public boolean onInterceptTouchEvent(MotionEvent event) {          if (!mEnable) {              return false;          }          try {              return mDragHelper.shouldInterceptTouchEvent(event);          } catch (ArrayIndexOutOfBoundsException e) {              // FIXME: handle exception              // issues #9              return false;          }      }         //处理ViewDragHelper   @Override      public boolean onTouchEvent(MotionEvent event) {          if (!mEnable) {              return false;          }          mDragHelper.processTouchEvent(event);          return true;      }  }  </code></pre>    <p>SwipeBackLayout 继承自 FrameLayout ,其中手势的操作是通过 ViewDragHelper 来实现的。在构造函数中一些必要的参数设置给 ViewDragHelper 。</p>    <pre>  <code class="language-java">public class SwipeBackLayout extends FrameLayout {     /**   * Edge flag indicating that the left edge should be affected.   */      public static final int EDGE_LEFT = ViewDragHelper.EDGE_LEFT;        /**   * Edge flag indicating that the right edge should be affected.   */      public static final int EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT;        /**   * Edge flag indicating that the bottom edge should be affected.   */      public static final int EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM;        /**   * Edge flag set indicating all edges should be affected.   */      public static final int EDGE_ALL = EDGE_LEFT | EDGE_RIGHT | EDGE_BOTTOM;   /**   * Default threshold of scroll   * 超过0.3f的屏幕比例的距离之后可以滑动出去了,临界值是0.3f   */      private static final float DEFAULT_SCROLL_THRESHOLD = 0.3f;       private static final int OVERSCROLL_DISTANCE = 10;     private float mScrimOpacity;        /**   * Edge being dragged   */      private int mTrackingEdge;         //滑动了距离和整个屏幕的的百分比     private float mScrollPercent;        private int mContentLeft;        private int mContentTop;       /**   * Threshold of scroll, we will close the activity, when scrollPercent over   * this value;   */      private float mScrollThreshold = DEFAULT_SCROLL_THRESHOLD;        private class ViewDragCallback extends ViewDragHelper.Callback {          private boolean mIsScrollOverValid;           //如果可拖动则返回true 否则为false          @Override          public boolean tryCaptureView(View view, int i) {//i是pointerId             //是否touch到了边缘              boolean ret = mDragHelper.isEdgeTouched(mEdgeFlag, i);             //哪个边缘被touch了              if (ret) {                  if (mDragHelper.isEdgeTouched(EDGE_LEFT, i)) {                      mTrackingEdge = EDGE_LEFT;                  } else if (mDragHelper.isEdgeTouched(EDGE_RIGHT, i)) {                      mTrackingEdge = EDGE_RIGHT;                  } else if (mDragHelper.isEdgeTouched(EDGE_BOTTOM, i)) {                      mTrackingEdge = EDGE_BOTTOM;                  }                 //回调出去                  if (mListeners != null && !mListeners.isEmpty()) {                      for (SwipeListener listener : mListeners) {                          listener.onEdgeTouch(mTrackingEdge);                      }                  }                  mIsScrollOverValid = true;              }              boolean directionCheck = false;             //是否达到了滑动的门槛              if (mEdgeFlag == EDGE_LEFT || mEdgeFlag == EDGE_RIGHT) {                  directionCheck = !mDragHelper.checkTouchSlop(ViewDragHelper.DIRECTION_VERTICAL, i);              } else if (mEdgeFlag == EDGE_BOTTOM) {                  directionCheck = !mDragHelper                          .checkTouchSlop(ViewDragHelper.DIRECTION_HORIZONTAL, i);              } else if (mEdgeFlag == EDGE_ALL) {                  directionCheck = true;              }              return ret & directionCheck;          }           //返回指定View在横向上能滑动的最大距离          @Override          public int getViewHorizontalDragRange(View child) {              return mEdgeFlag & (EDGE_LEFT | EDGE_RIGHT);          }           //返回指定View在纵向上能滑动的最大距离          @Override          public int getViewVerticalDragRange(View child) {              return mEdgeFlag & EDGE_BOTTOM;          }           //当子视图位置变化时,会回调这个函数          @Override          public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {              super.onViewPositionChanged(changedView, left, top, dx, dy);             //计算当前滑动比例              if ((mTrackingEdge & EDGE_LEFT) != 0) {                  mScrollPercent = Math.abs((float) left                          / (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth()));              } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {                  mScrollPercent = Math.abs((float) left                          / (mContentView.getWidth() + mShadowRight.getIntrinsicWidth()));              } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {                  mScrollPercent = Math.abs((float) top                          / (mContentView.getHeight() + mShadowBottom.getIntrinsicHeight()));              }              mContentLeft = left;              mContentTop = top;              invalidate();             //当滑动比例小于可滑动出去的时候,且mIsScrollOverValid已经为false的时候              if (mScrollPercent < mScrollThreshold && !mIsScrollOverValid) {                  mIsScrollOverValid = true;              }              if (mListeners != null && !mListeners.isEmpty()                      && mDragHelper.getViewDragState() == STATE_DRAGGING                      && mScrollPercent >= mScrollThreshold && mIsScrollOverValid) {                  mIsScrollOverValid = false;                 //回调出去,已经达到可以滑出结束Activity的标准了                  for (SwipeListener listener : mListeners) {                      listener.onScrollOverThreshold();                  }              }     //当比例大于等于1的时候,就可以关闭掉Activity了              if (mScrollPercent >= 1) {                  if (!mActivity.isFinishing()) {                      mActivity.finish();                      mActivity.overridePendingTransition(0, 0);                  }              }          }            //当手指从子视图松开时,会调用这个函数,同时返回在x轴和y轴上当前的速度          @Override          public void onViewReleased(View releasedChild, float xvel, float yvel) {              final int childWidth = releasedChild.getWidth();              final int childHeight = releasedChild.getHeight();                int left = 0, top = 0;              if ((mTrackingEdge & EDGE_LEFT) != 0) {//左边边缘                 //速度满足>=0且已经滑过了临界点0.3f,滑到最右边,不然滑到0的位置                  left = xvel > 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE : 0;              } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {//右边边缘      //速度满足>=0且已经滑过了临界点0.3f,滑到最左边,不然滑到0的位置                  left = xvel < 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? -(childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0;              } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {//上边边缘      //速度满足>=0且已经滑过了临界点0.3f,滑到最下边,不然滑到0的位置                  top = yvel < 0 || yvel == 0 && mScrollPercent > mScrollThreshold ? -(childHeight + mShadowBottom.getIntrinsicHeight() + OVERSCROLL_DISTANCE) : 0;              }     //移动View              mDragHelper.settleCapturedViewAt(left, top);             //刷新View              invalidate();          }              //返回一个值,告诉Helper,这个view能滑动的最大(或者负向最大)的横向坐标          @Override          public int clampViewPositionHorizontal(View child, int left, int dx) {              int ret = 0;              if ((mTrackingEdge & EDGE_LEFT) != 0) {                  ret = Math.min(child.getWidth(), Math.max(left, 0));              } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {                  ret = Math.min(0, Math.max(left, -child.getWidth()));              }              return ret;          }           //返回一个值,告诉Helper,这个view能滑动的最大(或者负向最大)的纵向坐标          @Override          public int clampViewPositionVertical(View child, int top, int dy) {              int ret = 0;              if ((mTrackingEdge & EDGE_BOTTOM) != 0) {                  ret = Math.min(0, Math.max(top, -child.getHeight()));              }              return ret;          }           //当边缘开始拖动的时候,会调用这个回调          @Override          public void onViewDragStateChanged(int state) {              super.onViewDragStateChanged(state);              if (mListeners != null && !mListeners.isEmpty()) {                  for (SwipeListener listener : mListeners) {                      listener.onScrollStateChange(state, mScrollPercent);                  }              }          }      }          @Override      public void computeScroll() {         //调用mDragHelper.settleCapturedViewAt(left, top)之后会进到这里          mScrimOpacity = 1 - mScrollPercent;          if (mDragHelper.continueSettling(true)) {              ViewCompat.postInvalidateOnAnimation(this);          }      }       @Override      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {          mInLayout = true;          if (mContentView != null) {              mContentView.layout(mContentLeft, mContentTop,                      mContentLeft + mContentView.getMeasuredWidth(),                      mContentTop + mContentView.getMeasuredHeight());          }          mInLayout = false;      }            @Override      public void requestLayout() {          if (!mInLayout) {              super.requestLayout();          }      }  }  </code></pre>    <p>在 ViewDragHelper.Callback 的手势判断中,处理的主要逻辑主要在 tryCaptureView 、 onViewPositionChanged 、 onViewReleased 三个方法中,分别是在准备滑动、滑动时、和放手的时候的逻辑。</p>    <p>在 tryCaptureView 中主要进行了边缘的判断,以及是否满足滑动条件;在 onViewPositionChanged 中计算了当前滑动距离与整个 ContentView 的距离的比例,是否超越临界值等;在 onViewReleased 中处理了手抬起之后的操作,比如将 View 滑归位或者滑出去等。</p>    <p>现在基本上了解了滑动的机制了,那么回过头来看看 attachToActivity :</p>    <pre>  <code class="language-java">public class SwipeBackLayout extends FrameLayout {          private View mContentView;        public void attachToActivity(Activity activity) {          mActivity = activity;          TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{                  android.R.attr.windowBackground          });          int background = a.getResourceId(0, 0);          a.recycle();            ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();    // 拿到decorView的第一个子view          ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);          decorChild.setBackgroundResource(background);         //把这个decorChild从decorView删除掉          decor.removeView(decorChild);         //将decorView添加到SwipeBackLayout中          addView(decorChild);         //将decorChild赋值给成员变量mContentView          setContentView(decorChild);          // 在DecorView下增加SwipeBackLayout          decor.addView(this);      }       /**   * Set up contentView which will be moved by user gesture   *   * @param view   */      private void setContentView(View view) {          mContentView = view;      }  }  </code></pre>    <p>通过 attachToActivity 将 decorView 中的 contentView 换成了 SwipeBackLayout ,而 contentView 则被添加到了 SwipeBackLayout 中。与正常的相比,之间多了一个 SwipeBackLayout 。</p>    <p>在滑动的时候哪些阴影是怎么出现的呢:</p>    <pre>  <code class="language-java">public class SwipeBackLayout extends FrameLayout {       private static final int DEFAULT_SCRIM_COLOR = 0x99000000;        private float mScrimOpacity;          private int mScrimColor = DEFAULT_SCRIM_COLOR;       private float mScrollPercent;       private Drawable mShadowLeft;      private Drawable mShadowRight;      private Drawable mShadowBottom;         private Rect mTmpRect = new Rect();       @Override      public void computeScroll() {          mScrimOpacity = 1 - mScrollPercent;          if (mDragHelper.continueSettling(true)) {              ViewCompat.postInvalidateOnAnimation(this);          }      }          @Override      protected boolean drawChild(Canvas canvas, View child, long drawingTime) {          final boolean drawContent = child == mContentView;            boolean ret = super.drawChild(canvas, child, drawingTime);          if (mScrimOpacity > 0 && drawContent                  && mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {              drawShadow(canvas, child);              drawScrim(canvas, child);          }          return ret;      }          private void drawScrim(Canvas canvas, View child) {         //得到alpha值          final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;         //得到新的alpha值          final int alpha = (int) (baseAlpha * mScrimOpacity);         //得到新的color          final int color = alpha << 24 | (mScrimColor & 0xffffff);    //绘制          if ((mTrackingEdge & EDGE_LEFT) != 0) {              canvas.clipRect(0, 0, child.getLeft(), getHeight());          } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {              canvas.clipRect(child.getRight(), 0, getRight(), getHeight());          } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {              canvas.clipRect(child.getLeft(), child.getBottom(), getRight(), getHeight());          }          canvas.drawColor(color);      }              private void drawShadow(Canvas canvas, View child) {          final Rect childRect = mTmpRect;         //得到当前View的位置          child.getHitRect(childRect);            if ((mEdgeFlag & EDGE_LEFT) != 0) {             //给drawable设置位置              mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top, childRect.left, childRect.bottom);             //设置透明度              mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA));             //画到canvas上              mShadowLeft.draw(canvas);          }    //给drawable设置位置、设置透明度、画到canvas上          if ((mEdgeFlag & EDGE_RIGHT) != 0) {              mShadowRight.setBounds(childRect.right, childRect.top, childRect.right + mShadowRight.getIntrinsicWidth(), childRect.bottom);              mShadowRight.setAlpha((int) (mScrimOpacity * FULL_ALPHA));              mShadowRight.draw(canvas);          }    //给drawable设置位置、设置透明度、画到canvas上          if ((mEdgeFlag & EDGE_BOTTOM) != 0) {              mShadowBottom.setBounds(childRect.left, childRect.bottom, childRect.right, childRect.bottom + mShadowBottom.getIntrinsicHeight());              mShadowBottom.setAlpha((int) (mScrimOpacity * FULL_ALPHA));              mShadowBottom.draw(canvas);          }      }  }  </code></pre>    <p>就这样,阴影就绘制出来了。</p>    <p>再看看 scrollToFinishActivity :</p>    <pre>  <code class="language-java">public class SwipeBackLayout extends FrameLayout {   /**   * Scroll out contentView and finish the activity   */      public void scrollToFinishActivity() {         //得到contentView的宽高          final int childWidth = mContentView.getWidth();          final int childHeight = mContentView.getHeight();    //要移动到的位置          int left = 0, top = 0;          if ((mEdgeFlag & EDGE_LEFT) != 0) {              left = childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE;              mTrackingEdge = EDGE_LEFT;          } else if ((mEdgeFlag & EDGE_RIGHT) != 0) {              left = -childWidth - mShadowRight.getIntrinsicWidth() - OVERSCROLL_DISTANCE;              mTrackingEdge = EDGE_RIGHT;          } else if ((mEdgeFlag & EDGE_BOTTOM) != 0) {              top = -childHeight - mShadowBottom.getIntrinsicHeight() - OVERSCROLL_DISTANCE;              mTrackingEdge = EDGE_BOTTOM;          }            mDragHelper.smoothSlideViewTo(mContentView, left, top);          invalidate();      }       @Override      public void computeScroll() {         //调用mDragHelper.smoothSlideViewTo(mContentView, left, top);之后进到这里          mScrimOpacity = 1 - mScrollPercent;          if (mDragHelper.continueSettling(true)) {              ViewCompat.postInvalidateOnAnimation(this);          }      }  }  </code></pre>    <h3>Utils</h3>    <pre>  <code class="language-java">public class Utils {      private Utils() {      }       /**   * Convert a translucent themed Activity   * {@link android.R.attr#windowIsTranslucent} back from opaque to   * translucent following a call to   * {@link #convertActivityFromTranslucent(android.app.Activity)} .   * <p>   * Calling this allows the Activity behind this one to be seen again. Once   * all such Activities have been redrawn   * <p>   * This call has no effect on non-translucent activities or on activities   * with the {@link android.R.attr#windowIsFloating} attribute.   */      public static void convertActivityToTranslucent(Activity activity) {          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {              convertActivityToTranslucentAfterL(activity);          } else {              convertActivityToTranslucentBeforeL(activity);          }      }       /**   * Calling the convertToTranslucent method on platforms before Android 5.0   */      public static void convertActivityToTranslucentBeforeL(Activity activity) {          try {              Class<?>[] classes = Activity.class.getDeclaredClasses();              Class<?> translucentConversionListenerClazz = null;              for (Class clazz : classes) {                  if (clazz.getSimpleName().contains("TranslucentConversionListener")) {                      translucentConversionListenerClazz = clazz;                  }              }              Method method = Activity.class.getDeclaredMethod("convertToTranslucent",                      translucentConversionListenerClazz);              method.setAccessible(true);              method.invoke(activity, new Object[] {                  null              });          } catch (Throwable t) {          }      }        /**   * Calling the convertToTranslucent method on platforms after Android 5.0   */      private static void convertActivityToTranslucentAfterL(Activity activity) {          try {              Method getActivityOptions = Activity.class.getDeclaredMethod("getActivityOptions");              getActivityOptions.setAccessible(true);              Object options = getActivityOptions.invoke(activity);                Class<?>[] classes = Activity.class.getDeclaredClasses();              Class<?> translucentConversionListenerClazz = null;              for (Class clazz : classes) {                  if (clazz.getSimpleName().contains("TranslucentConversionListener")) {                      translucentConversionListenerClazz = clazz;                  }              }              Method convertToTranslucent = Activity.class.getDeclaredMethod("convertToTranslucent",                      translucentConversionListenerClazz, ActivityOptions.class);              convertToTranslucent.setAccessible(true);              convertToTranslucent.invoke(activity, null, options);          } catch (Throwable t) {          }      }  }  </code></pre>    <p>通过反射改变 Activity 的属性值。</p>    <p> </p>    <p>来自: http://yydcdut.com/2016/05/08/swipebacklayout-analyse/</p>