SwipeBackLayout源码解析
2017-12-19 09:47
344 查看
SwipeBackLayout是一个开源的实现了Activity滑动返回的库,当Activity滑动返回时,另一个Activity界面逐渐显示。效果图如下:
SwipeBackLayout
从上图中看出,滑动的是一个Activity上最外层的ViewGroup,使用帧布局比较合适,在SwipeBackLayout的源码中,他也是继承自帧布局。在帧布中定义了一些常量和一些成员变量,例如:滑动速率,屏幕边沿标志,滑动过程中的一些参数等等。在构造方法里面进行了一些自定义属性的初始化内容。在SwipeBackLayout控件中有一个ViewDragHelper,他是实现手势触摸识别的核心类,要研究SwipeBackLayout的源码,首先从ViewDragHelper进行着手。
ViewDragHelper
在处理触摸事件中常常使用事件分发机制进行分析,对于复杂的事件处理使用这种机制分析起来很麻烦,正常情况下需要响应down事件,记录当时触摸的点,然后根据这个点,判断触摸到了哪个view,其次响应move事件,得到移动的x轴、y轴的偏移量,最后将偏移量作用于view。因此使用ViewDragHelper进行处理,他是官方v4包下提供的一个类,提供了一系列的方法和状态跟踪,能够简化事件处理流程,可以把以上事件处理全部交给ViewDragHelper进行处理。
1、将事件拦截交给shouldInterceptTouchEvent(MotionEvent ev)方法
2、将事件触摸交给processTouchEvent(MotionEvent ev),并且返回true,响应此事件。
ViewDragCallback
ViewDragCallback继承自ViewDragHelper.Callback拖拽效果实现类,核心方法如下:
tryCaptureView
表示该view是否支持滑动,返回true表示支持滑动, false表示不支持滑动,包含两个参数:
参数一:支持滑动的view
参数二:多指触控
实现步骤:
1、是否支持边沿触控
2、判断是那侧的边沿触控,设置触控回调
3、判断是否为指定的滑动,防止乱划,斜着滑屏
4、是边缘滑动并且满足可以滑动的时候,contentview才可以拖动
clampViewPositionHorizontal
该类是获取child需要被移动到的x轴位置距离,接收三个参数:
参数一:拖动的view
参数二:x轴坐标位置
参数三:x轴移动位置
实现步骤:
1、如果为左边界滑动,边界限制为(最小值为0,最大值为contentview的宽度)
2、如果为右边界滑动,边界限制为(最小值为-contentView的宽度,最大值为0)
clampViewPositionVertical
该类是获取child需要被移动到的y轴位置距离,接收三个参数:
参数一:拖动的view
参数二:y轴坐标位置
参数三:y轴移动位置
实现步骤:
如果为底部滑动,边界限制为(最小值为-contentView的高度,最大值为0);
getViewHorizontalDragRange
该类表示是否支持水平滑动,返回结果>0,支持水平拖动;<0,不支持,接收一个参数:
参数一:拖动的view
实现步骤:
如果指定的滑动为左右滑动,则支持
getViewVerticalDragRange
该类表示是否支持竖直滑动,返回结果>0,支持竖直拖动;<0,不支持,接收一个参数:
参数一:拖动的view
实现步骤:
滑动边沿为底部时候支持竖直滑动
onViewPositionChanged
当位置发生改变时候回调,参数含义,接收三个参数:
参数一:拖动的view
参数二三:left,top变化时新的x左/y顶坐标
参数四五:从旧到新位置的偏移量
实现步骤:
1、获取一个滑过位置占屏幕总宽或者高的百分比
2、获取拖动过后的left跟top,然后调用invalidate会重新绘制ViewGroup,在onLayout方法中重新摆放子控件位置
3、滑动超过设置自动滑动距离的临界值时回调
4、当滑动完毕时候,关闭Activity
onViewReleased
前被捕获的View释放之后回调,即手指抬起的回调,接收三个参数:
参数一:拖动的view
参数二:x轴方向瞬时速度
参数三:y轴方向瞬时速度
实现步骤:
1、计算自动滚动到指定的left跟top位置
2、设置自动滚动到指定的left跟top位置
3、重绘
onViewDragStateChanged
当ViewDragHelper状态发生变化时回调,接收一个参数:
参数一:滑动状态表示值
实现步骤:设置回调通知
以上就是SwipeBackLayout核心类ViewDragHelper中所处理的内容,每个方法都有总结,并且源码中都有详细的备注。
在使用ViewDragHelper.Callback处理完毕滑动事件之后就需要对View进行重新位置的摆放和重绘了。需要重写onLayout,drawChild两个方法,做出相应处理。
onLayout
onLayout是指在前面Callback的onViewPositionChanged方法中已经获得滑动后View的left、top、mScrollPercent,在调用invalidate时候会调用onLayout方法,重新摆放控件位置。
实现内容:
mContentView调用layout方法,进行设置。
drawChild
drawChild是指当View滑动过后,滑过空间部分半透明阴影部分的绘制。
实现内容:
1、获取child的坐标范围,绘制阴影部分,动态设置透明度
2、阴影部分裁剪,防止其余部分被显示
attachToActivity
通过阴影部分的绘制,以及透明度的动态设置,就会出现当View滑动时,滑动过的部分渐渐变亮,只到完全显示。现在来看自定义的View如何设置到Activity上去,使Activity变成可拖动的。步骤如下:
1、获取Activity顶级视图
2、获得Activity界面所用的xml文件的根view
3、给Activity的根View设置背景
4、从Activity的顶级视图移除activity的xml的根view
5、给SwipeBackLayout添加子view,作为SwipeBackLayout的第一个view
6、把decorChild当成SwipeBackLayout的contentView进行设置
7、把SwipeBackLayout作为Activity顶级视图的子View进行设置
版本兼容
SwipeBackLayout绘在5.0下的版本出现透明度显示问题,为了兼容5.0以下的版本,在convertActivityToTranslucent做了一个版本兼容。在Utils类中做了转换:
1、通过关键字段反射获得透明度设置的类名,方法名称,
2、传递参数手动调用透明度设置的方法。
3、另外在使用时候还需要在应用的主题下添加:
<item name="android:windowIsTranslucent">true</item>
Window设置
Activity的Window默认颜色是白色的,如果不进行设置就会遮住下面的Activity,所在加载布局之前需要设置当前窗口颜色。在SwipeBackActivityHelper类中有个onActivityCreate方法就做了如下处理:
1、设置当前的Window为透明色
2、去掉Activity根视图的背景颜色
以上就是SwipeBackLayout的实现原理,接下来看看SwipeBackActivity。
SwipeBackActivity
1、SwipeBackActivity 需要实现接口SwipeBackActivityBase,重写以下三个方法:
(1) getSwipeBackLayout:获取SwipeBackLayout控件
(2)setSwipeBackEnable:设置是否支持手势滑动
(3)scrollToFinishActivity:滑动结束后关闭Activity
2、onCreate
这里重写onActivityCreate方法,调用SwipeBackActivityHelper中的onActivityCreate方法:
(1)加载布局前设置window的颜色
(2)加载布局前解决滑动时候5.0之前黑屏闪动的bug兼容
3、onPostCreate
这里调用attachToActivity方法,在onStart之后把当前的SwipeBackLayout设置为activity的顶层view。
4、其他设置,例如:获取SwipeBackLayout布局,设置是否支持触摸等。
用法
1、引入库文件或者添加依赖
compile 'me.imid.swipebacklayout.lib:library:1.1.0'
2、继承自SwipeBackActivity
SwipeBackLayout
从上图中看出,滑动的是一个Activity上最外层的ViewGroup,使用帧布局比较合适,在SwipeBackLayout的源码中,他也是继承自帧布局。在帧布中定义了一些常量和一些成员变量,例如:滑动速率,屏幕边沿标志,滑动过程中的一些参数等等。在构造方法里面进行了一些自定义属性的初始化内容。在SwipeBackLayout控件中有一个ViewDragHelper,他是实现手势触摸识别的核心类,要研究SwipeBackLayout的源码,首先从ViewDragHelper进行着手。
ViewDragHelper
在处理触摸事件中常常使用事件分发机制进行分析,对于复杂的事件处理使用这种机制分析起来很麻烦,正常情况下需要响应down事件,记录当时触摸的点,然后根据这个点,判断触摸到了哪个view,其次响应move事件,得到移动的x轴、y轴的偏移量,最后将偏移量作用于view。因此使用ViewDragHelper进行处理,他是官方v4包下提供的一个类,提供了一系列的方法和状态跟踪,能够简化事件处理流程,可以把以上事件处理全部交给ViewDragHelper进行处理。
1、将事件拦截交给shouldInterceptTouchEvent(MotionEvent ev)方法
@Override public boolean onInterceptTouchEvent(MotionEvent event) { if (!mEnable) { return false; } try { return mDragHelper.shouldInterceptTouchEvent(event); } catch (ArrayIndexOutOfBoundsException e) { return false; } }
2、将事件触摸交给processTouchEvent(MotionEvent ev),并且返回true,响应此事件。
@Override public boolean onTouchEvent(MotionEvent event) { if (!mEnable) { return false; } mDragHelper.processTouchEvent(event); return true; }
ViewDragCallback
ViewDragCallback继承自ViewDragHelper.Callback拖拽效果实现类,核心方法如下:
tryCaptureView
表示该view是否支持滑动,返回true表示支持滑动, false表示不支持滑动,包含两个参数:
参数一:支持滑动的view
参数二:多指触控
实现步骤:
1、是否支持边沿触控
2、判断是那侧的边沿触控,设置触控回调
3、判断是否为指定的滑动,防止乱划,斜着滑屏
4、是边缘滑动并且满足可以滑动的时候,contentview才可以拖动
@Override public boolean tryCaptureView(View view, int i) { //是否支持边沿触摸 boolean ret = mDragHelper.isEdgeTouched(mEdgeFlag, i); 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; } //是边缘滑动并且满足可以滑动的时候,contentview才可以拖动 return ret & directionCheck; }
clampViewPositionHorizontal
该类是获取child需要被移动到的x轴位置距离,接收三个参数:
参数一:拖动的view
参数二:x轴坐标位置
参数三:x轴移动位置
实现步骤:
1、如果为左边界滑动,边界限制为(最小值为0,最大值为contentview的宽度)
2、如果为右边界滑动,边界限制为(最小值为-contentView的宽度,最大值为0)
@Override public int clampViewPositionHorizontal(View child, int left, int dx) { //如果为左边界滑动,边界限制为(最小值为0,最大值为contentview的宽度) //如果为右边界滑动,边界限制为(最小值为-contentView的宽度,最大值为0) 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; }
clampViewPositionVertical
该类是获取child需要被移动到的y轴位置距离,接收三个参数:
参数一:拖动的view
参数二:y轴坐标位置
参数三:y轴移动位置
实现步骤:
如果为底部滑动,边界限制为(最小值为-contentView的高度,最大值为0);
@Override public int clampViewPositionVertical(View child, int top, int dy) { //如果为底部滑动,边界限制为(最小值为-contentView的高度,最大值为0) int ret = 0; if ((mTrackingEdge & EDGE_BOTTOM) != 0) { ret = Math.min(0, Math.max(top, -child.getHeight())); } return ret; }
getViewHorizontalDragRange
该类表示是否支持水平滑动,返回结果>0,支持水平拖动;<0,不支持,接收一个参数:
参数一:拖动的view
实现步骤:
如果指定的滑动为左右滑动,则支持
@Override public int getViewHorizontalDragRange(View child) { //如果指定的滑动是做边沿或者有边沿则支持左右滑动 return mEdgeFlag & (EDGE_LEFT | EDGE_RIGHT); }
getViewVerticalDragRange
该类表示是否支持竖直滑动,返回结果>0,支持竖直拖动;<0,不支持,接收一个参数:
参数一:拖动的view
实现步骤:
滑动边沿为底部时候支持竖直滑动
@Override public int getViewVerticalDragRange(View child) { //滑动边沿为底部时候支持竖直滑动 return mEdgeFlag & EDGE_BOTTOM; }
onViewPositionChanged
当位置发生改变时候回调,参数含义,接收三个参数:
参数一:拖动的view
参数二三:left,top变化时新的x左/y顶坐标
参数四五:从旧到新位置的偏移量
实现步骤:
1、获取一个滑过位置占屏幕总宽或者高的百分比
2、获取拖动过后的left跟top,然后调用invalidate会重新绘制ViewGroup,在onLayout方法中重新摆放子控件位置
3、滑动超过设置自动滑动距离的临界值时回调
4、当滑动完毕时候,关闭Activity
@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=拖动过后的left/(mContentView的宽度+左边阴影的宽度) 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())); } //获取拖动过后的left跟top,然后调用invalidate会重新绘制ViewGroup,在onLayout方法中重新摆放子控件位置 mContentLeft = left; mContentTop = top; invalidate(); if (mScrollPercent < mScrollThreshold && !mIsScrollOverValid) { mIsScrollOverValid = true; } //滑动超过设置距离的临界值时回调 if (mListeners != null && !mListeners.isEmpty() && mDragHelper.getViewDragState() == STATE_DRAGGING && mScrollPercent >= mScrollThreshold && mIsScrollOverValid) { mIsScrollOverValid = false; for (SwipeListener listener : mListeners) { listener.onScrollOverThreshold(); } } //当activity滑动的完全滑动完毕时候,关闭activity if (mScrollPercent >= 1) { if (!mActivity.isFinishing()) { mActivity.finish(); mActivity.overridePendingTransition(0, 0); } } }
onViewReleased
前被捕获的View释放之后回调,即手指抬起的回调,接收三个参数:
参数一:拖动的view
参数二:x轴方向瞬时速度
参数三:y轴方向瞬时速度
实现步骤:
1、计算自动滚动到指定的left跟top位置
2、设置自动滚动到指定的left跟top位置
3、重绘
@Override public void onViewReleased(View releasedChild, float xvel, float yvel) { final int childWidth = releasedChild.getWidth(); final int childHeight = releasedChild.getHeight(); //满足条件时候是设置left,top的值,条件内容:如果释放位置的滑动距离大于设定距离自动滑动关闭, //否则自动滑动到开始位置 int left = 0, top = 0; if ((mTrackingEdge & EDGE_LEFT) != 0) { //当为左边滑动的时候 //x轴上滑动的速度>=0并且滑动的距离到达总距离自定的0.3f的时候, //left即为(content的宽度+左阴影的宽度+超出滑动距离的offset) left = xvel > 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE : 0; } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { left = xvel < 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? -(childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0; } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) { top = yvel < 0 || yvel == 0 && mScrollPercent > mScrollThreshold ? -(childHeight + mShadowBottom.getIntrinsicHeight() + OVERSCROLL_DISTANCE) : 0; } //自动滚动到指定的left跟top位置 mDragHelper.settleCapturedViewAt(left, top); invalidate(); }
onViewDragStateChanged
当ViewDragHelper状态发生变化时回调,接收一个参数:
参数一:滑动状态表示值
实现步骤:设置回调通知
@Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); if (mListeners != null && !mListeners.isEmpty()) { for (SwipeListener listener : mListeners) { listener.onScrollStateChange(state, mScrollPercent); } } }
以上就是SwipeBackLayout核心类ViewDragHelper中所处理的内容,每个方法都有总结,并且源码中都有详细的备注。
在使用ViewDragHelper.Callback处理完毕滑动事件之后就需要对View进行重新位置的摆放和重绘了。需要重写onLayout,drawChild两个方法,做出相应处理。
onLayout
onLayout是指在前面Callback的onViewPositionChanged方法中已经获得滑动后View的left、top、mScrollPercent,在调用invalidate时候会调用onLayout方法,重新摆放控件位置。
实现内容:
mContentView调用layout方法,进行设置。
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { mInLayout = true; if (mContentView != null) //对控件进行重新定位调用onLayout()这个方法mContentLeft,mContentTop为拖动后的宽高 mContentView.layout(mContentLeft, mContentTop, mContentLeft + mContentView.getMeasuredWidth(), mContentTop + mContentView.getMeasuredHeight()); mInLayout = false; }
drawChild
drawChild是指当View滑动过后,滑过空间部分半透明阴影部分的绘制。
实现内容:
1、获取child的坐标范围,绘制阴影部分,动态设置透明度
2、阴影部分裁剪,防止其余部分被显示
/** * 遍历了所有子View,每个子View都调用了drawChild这个方法 */ @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; }
attachToActivity
通过阴影部分的绘制,以及透明度的动态设置,就会出现当View滑动时,滑动过的部分渐渐变亮,只到完全显示。现在来看自定义的View如何设置到Activity上去,使Activity变成可拖动的。步骤如下:
1、获取Activity顶级视图
2、获得Activity界面所用的xml文件的根view
3、给Activity的根View设置背景
4、从Activity的顶级视图移除activity的xml的根view
5、给SwipeBackLayout添加子view,作为SwipeBackLayout的第一个view
6、把decorChild当成SwipeBackLayout的contentView进行设置
7、把SwipeBackLayout作为Activity顶级视图的子View进行设置
public void attachToActivity(Activity activity) { mActivity = activity; // 返回一个与主题Theme定义的 attrs数组对应的typedArray类型数组 TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{ android.R.attr.windowBackground }); // 获取typedArray数组中指定位置的资源id值 int background = a.getResourceId(0, 0); a.recycle(); // 返回顶层窗口根视图 ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView(); //得到我activity的xml的根view ViewGroup decorChild = (ViewGroup) decor.getChildAt(0); // 给顶层窗口根视图的根view设置背景资源 decorChild.setBackgroundResource(background); // 移除activity的xml的根view decor.removeView(decorChild); //给SwipeBackLayout添加子view,作为SwipeBackLayout的第一个view addView(decorChild); //把decorChild当成SwipeBackLayout的contentView进行设置 setContentView(decorChild); decor.addView(this); }这样View就和Activiy建立起关联了,只需要把decorChild传递进去,SwipeBackLayout作为他的父View,结构如下:
版本兼容
SwipeBackLayout绘在5.0下的版本出现透明度显示问题,为了兼容5.0以下的版本,在convertActivityToTranslucent做了一个版本兼容。在Utils类中做了转换:
public static void convertActivityToTranslucent(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { convertActivityToTranslucentAfterL(activity); } else { convertActivityToTranslucentBeforeL(activity); } }主要内容如下:
1、通过关键字段反射获得透明度设置的类名,方法名称,
2、传递参数手动调用透明度设置的方法。
3、另外在使用时候还需要在应用的主题下添加:
<item name="android:windowIsTranslucent">true</item>
Window设置
Activity的Window默认颜色是白色的,如果不进行设置就会遮住下面的Activity,所在加载布局之前需要设置当前窗口颜色。在SwipeBackActivityHelper类中有个onActivityCreate方法就做了如下处理:
1、设置当前的Window为透明色
2、去掉Activity根视图的背景颜色
public void onActivityCreate() { //加载布局之前设置当前窗口颜色 mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mActivity.getWindow().getDecorView().setBackgroundDrawable(null); ……………………………………………………………………………………省略部分代码………………………………………………………………………………… }
以上就是SwipeBackLayout的实现原理,接下来看看SwipeBackActivity。
SwipeBackActivity
1、SwipeBackActivity 需要实现接口SwipeBackActivityBase,重写以下三个方法:
(1) getSwipeBackLayout:获取SwipeBackLayout控件
(2)setSwipeBackEnable:设置是否支持手势滑动
(3)scrollToFinishActivity:滑动结束后关闭Activity
2、onCreate
这里重写onActivityCreate方法,调用SwipeBackActivityHelper中的onActivityCreate方法:
(1)加载布局前设置window的颜色
(2)加载布局前解决滑动时候5.0之前黑屏闪动的bug兼容
3、onPostCreate
这里调用attachToActivity方法,在onStart之后把当前的SwipeBackLayout设置为activity的顶层view。
4、其他设置,例如:获取SwipeBackLayout布局,设置是否支持触摸等。
用法
1、引入库文件或者添加依赖
compile 'me.imid.swipebacklayout.lib:library:1.1.0'
2、继承自SwipeBackActivity
相关文章推荐
- Android滑动返回-swipebacklayout解析
- [重写开源框架源码]SwipeBackLayout实现全屏滑动
- SwipeBackLayout 和SwipeBackActivity最完全解析
- Android LinearLayout(7.1) 源码解析
- Android LayoutInflater源码解析
- 安卓滑动返回 SwipeBackLayout
- 类似IOS的滑动返回上一级,SwipeBackLayout-android的滑动返回类库
- 滑动返回类库SwipeBackLayout的使用问题,解决返回黑屏,和看到桌面
- Android LayoutInflater 源码解析
- 解决SwipeBackLayout滑动返回时显示桌面而不是显示上一层的Bug
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- android应用程序窗口框架学习(2)-view绘制流程源代码解析-setContentView与LayoutInflater加载解析机制源码分析
- 像素缓冲区对象(PBO)的异步Read-Back 源码解析
- SwipeRefreshLayout 源码关键方法解析
- [开源学习]SwipeMenuListView源码实现过程解析
- Android SwipeBackLayout返回黑屏的处理
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android setContentView与LayoutInflater加载解析机制源码分析
- 滑动返回SwipeBackLayout