关于Android View体系里面的相关知识,可能会比较乱,先都罗列出来再说,后期再做整理。
自己做的笔记,不太适合学习用,大家看看就好,有问题烦请指正。
几个常用对象之间的关系
Activity
间接继承自 Context
activity.getWindow
获取到的是 PhoneWindow
,继承自 Window
(抽象类)
PhoneWIndow
中持有mDecor
① 和 mContentParent
②:
①:mDecor = new DecorView()
,间接继承ViewGroup
DecorView extends FrameLayout extends ViewGroup
②:mContentParent = generateLayout(mDecor)
;
`protected ViewGroup generateLayout(DecorView decor)`
`mContentParent `是`ViewGroup`对象
setContentView的执行
getWindow().setContentView(resID)
,其中window
对象在Activity
的attach()
方法中初始化 (PhoneWindow
)PhoneWindow.setCOntentView()
源码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
//阳氏翻译:在insatllDecor()过程中可能会设置FEATURE_CONTENT_TRANSITIONS的值,直到theme属性明确
if (mContentParent == null) {
installDecor();//这里面new了mDecor和mContentParent俩对象
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);//布局加载到mContentParent中去
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}关注后两个注释即可,一个初始化了
Decor
和ContentParent
;一个加载了布局。installDecor()
中有这两句:①:mDecor = generateDecor();
②:`mContentParent = generateLayout(mDecor);`
大致如图:
事件分发机制
白话
依上图可见,手指触摸屏幕瞬间会触发Activity.dispatchTouchEvent
,依次传递到window、decor直到ViewGroup.dispatchTouchEvent()
方法中来。
跟着,ViewGroup
的dispatchTouchEvent
方法中一堆逻辑判断,判断是否拦截。
如果拦截,跳转到ViewGroup.onTouchEvent()
;
如果不拦截,则遍历child,执行child.dispatchTouchEvent()
方法;
child是view的话,在 `view.dispatchTouchEvent()`方法中先判断有无 `touchListener`,
有的话执行`touchLIstener`,否则 执行 `view.onTouchEvent()`方法
代码可抽象成这样:
1 | //不是源码!!!!! |
后续: view.onTouchEvent()
中,若view的clickable
或LongClickable
为true
且 ACTION_UP
,则调用 performClick()
方法;
`performClick()`方法中对`onCLickListener` 进行了响应:`mListenerInfo.mOnClickListener.onClick(this)`
图示
对了,默认情况下ViewGroup.onInterceptTouchEvent()
返回false,即默认可以一条路走下来。
View的工作流程
当我们调用Activity的startActivity()
方法时,最终是调用ActivityThread.handleLaunchActivity
方法 来创建Activity的。
1 | public Activity handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { |
1 | final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { |
可以看出handleResumeActivity
中最终是把布局委托给了WindowManager
去创建。
WindowManager
是一个接口,主要实现是在WindowManagerImpl
中:
1 |
|
跟着又用到了mGlobal.addView()
方法,mGlobal
是windowManagerGlobal
对象,windowManagerGlobal
中addView
方法如下:
1 | public void addView(View view, ViewGroup.LayoutParams params, |
②中的root即ViewRootImpl
,ViewRootImpl
还有一个方法 PerformTraversals()
。这个方法使得ViewTree开始View的工作流程。performMeasure、performLayout、performDraw方法都在里面。对应measure、layout、draw方法。
提一句,里面的performMeasure()
会传入两个参数 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
MeasureSpec的最顶层
PerformTraversals
中,有如下代码:
1 | if(!mStopped){ |
MeasureSpec
是一个32位的int值,前两位表示mode,后30位代表size。这个应该要展开来讲,不清楚的可以网上看看。
这里的performMeasure
中传入的两个值就是最顶层的MeasureSpec
。由 getRootMeasureSpec()
方法获取:
1 | private static int getRootMeasureSpec(int windowSize, int rootDimension) { |
windowSize
是窗口的尺寸,(1920*1080或者是2660**1440等,根据设备而定);
然后根据LayoutParams
具体是什么来设置measureSpec
。代码写的很清楚。
再往下走,看performMeasure()
方法:
1 | private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { |
关键就那一句,熟悉的measure方法开始了。
不要忘了mView是谁,没错,就是上文(ViewRootImpl.setView()
)中传来的view,上上文(windowManager.addView()
)中传来的view——mDecor。
如果你记忆力还比较好的话,你应该记得文章开头的地方列出了decor的继承关系——继承自ViewGroup。