Github: SwipeBackLayout 分析版本: e4ddae6
SwipeBackLayout 是一个仿 IOS 通过手势退出界面的开源库。
SwipeBackLayout 可以通过在左、右和下边缘来拖动整个 Activity 达到退出 Activity 的效果。
添加到 Gradle :
compile 'me.imid.swipebacklayout.lib:library:1.0.0'
继承 SwipeBackActivity
:
public class DemoActivity extends SwipeBackActivity {
}
onCreate
中 setContentView()
照常使用 getSwipeBackLayout()
定制 SwipeBackLayout 在 styles.xml
中的主题中添加:
<item name="android:windowIsTranslucent">true</item>
需要在项目中添加最新的 supportV4 包
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);
}
});
}
...
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();
}
}
在 SwipeBackActivity
中实现了 SwipeBackActivityBase
接口,在 Activity 的生命周期函数 onCreate()
中创建了 SwipeBackActivityHelper
对象, 该类的作用是设置 Activity 的透明和在 DecorView 中替换 SwipeBackLayout 。 onPostCreate()
是在 Activity 完全运行起来之后才会被调用。其中 findViewById()
方法进行了判断,首先在 Activity 的 contentView 中获取,获取不到再到 SwipeBackLayout
中获取。
在 SwipeBackActivity
的 onCreate()
中的调用方法:
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() {
}
});
}
}
在 onActivityCreate
中主要就是将 window 、 decorView 的背景设置为透明的。
在 SwipeBackActivity
的 onPostCreate()
中的调用方法:
public class SwipeBackActivityHelper {
public void onPostCreate() {
mSwipeBackLayout.attachToActivity(mActivity);
}
}
在 attachToActivity
中的操作就是将 decorView 中的 childView 换成 SwipeBackLayout
,然后将 childView 添加到 SwipeBackLayout
中。
其他的方法:
public class SwipeBackActivityHelper {
public View findViewById(int id) {
if (mSwipeBackLayout != null) {
return mSwipeBackLayout.findViewById(id);
}
return null;
}
public SwipeBackLayout getSwipeBackLayout() {
return mSwipeBackLayout;
}
}
SwipeBackLayout
是一个 View ,可以从构造函数开始看:
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;
}
}
SwipeBackLayout
继承自 FrameLayout ,其中手势的操作是通过 ViewDragHelper
来实现的。在构造函数中一些必要的参数设置给 ViewDragHelper
。
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();
}
}
}
在 ViewDragHelper.Callback
的手势判断中,处理的主要逻辑主要在 tryCaptureView
、 onViewPositionChanged
、 onViewReleased
三个方法中,分别是在准备滑动、滑动时、和放手的时候的逻辑。
在 tryCaptureView
中主要进行了边缘的判断,以及是否满足滑动条件;在 onViewPositionChanged
中计算了当前滑动距离与整个 ContentView 的距离的比例,是否超越临界值等;在 onViewReleased
中处理了手抬起之后的操作,比如将 View 滑归位或者滑出去等。
现在基本上了解了滑动的机制了,那么回过头来看看 attachToActivity
:
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;
}
}
通过 attachToActivity
将 decorView 中的 contentView 换成了 SwipeBackLayout
,而 contentView 则被添加到了 SwipeBackLayout
中。与正常的相比,之间多了一个 SwipeBackLayout
。
在滑动的时候哪些阴影是怎么出现的呢:
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);
}
}
}
就这样,阴影就绘制出来了。
再看看 scrollToFinishActivity
:
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);
}
}
}
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) {
}
}
}
通过反射改变 Activity 的属性值。