关于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的执行

  1. getWindow().setContentView(resID),其中window对象在 Activityattach()方法中初始化 (PhoneWindow

  2. 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
    @Override
    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;
    }

    关注后两个注释即可,一个初始化了DecorContentParent;一个加载了布局。

    installDecor()中有这两句:①:mDecor = generateDecor();

                                                ②:`mContentParent = generateLayout(mDecor);`

    大致如图:

activity构成.png

事件分发机制

白话

依上图可见,手指触摸屏幕瞬间会触发Activity.dispatchTouchEvent,依次传递到window、decor直到ViewGroup.dispatchTouchEvent()方法中来。

跟着,ViewGroupdispatchTouchEvent方法中一堆逻辑判断,判断是否拦截。

如果拦截,跳转到ViewGroup.onTouchEvent();

如果不拦截,则遍历child,执行child.dispatchTouchEvent()方法;

    child是view的话,在 `view.dispatchTouchEvent()`方法中先判断有无 `touchListener`,

            有的话执行`touchLIstener`,否则  执行 `view.onTouchEvent()`方法

代码可抽象成这样

1
2
3
4
5
6
7
8
9
10
11
//不是源码!!!!!
public boolean dispatchTouchEvent(MotionEvent ev){
boolean result = false;
//这里就是判断是否拦截的一堆逻辑
if(onInterceptTouchEvent(ev)){
result = super.onTouchEvent(ev);
}else{
result = child.dispatchTouchEvent(ev)
}
return result;
}

后续view.onTouchEvent()中,若view的clickableLongClickabletrueACTION_UP ,则调用 performClick()方法;

        `performClick()`方法中对`onCLickListener` 进行了响应:`mListenerInfo.mOnClickListener.onClick(this)`

图示

事件分发流程

对了,默认情况下ViewGroup.onInterceptTouchEvent()返回false,即默认可以一条路走下来。

View的工作流程

当我们调用Activity的startActivity()方法时,最终是调用ActivityThread.handleLaunchActivity方法 来创建Activity的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Activity handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// Initialize before creating the activity
WindowManagerGlobal.initialize();

/** performLaunchActivity: Core implementation(核心实现) of activity launch. */
//这里书上说会调用到onCreate()方法中来,我看源码中调用到了attach()方法
Activity a = performLaunchActivity(r, customIntent);

if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
//handleResumeActivity 代码在下面
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);

...
}
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
27
28
29
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
//Resume the activity.
ActivityClientRecord r = performResumeActivity(token, clearHide);

if (r != null) {
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();//getWindow
View decor = r.window.getDecorView();//getDecooView
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();//getWindowManager
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//WindowManager是接口,实际调用的WindowManagerImpl.addView()
//不管怎么说,这里至少已经把decor传到了windowmanager中去了
//不过界面上看不出来,必须要等measure、layout、draw 三板斧走完才能看到
wm.addView(decor, l);
}
...
}
...
}
}

可以看出handleResumeActivity中最终是把布局委托给了WindowManager去创建。

WindowManager是一个接口,主要实现是在WindowManagerImpl中:

1
2
3
4
5
6
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}

跟着又用到了mGlobal.addView()方法,mGlobalwindowManagerGlobal对象,windowManagerGlobaladdView方法如下:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {

...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
...
ViewRootImpl root;
View panelParentView = null;

synchronized (mLock) {
...
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}

//①初始化ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

mViews.add(view);
mRoots.add(root);
mParams.add(wparams);

// do this last because it fires off messages to start doing things
try {
//②穿进去的view ,即是上文中的Decor
//至此,Decor就传入到了window中去了
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}

②中的root即ViewRootImpl,ViewRootImpl还有一个方法 PerformTraversals()。这个方法使得ViewTree开始View的工作流程。performMeasure、performLayout、performDraw方法都在里面。对应measure、layout、draw方法。

提一句,里面的performMeasure()会传入两个参数 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

MeasureSpec的最顶层

PerformTraversals中,有如下代码:

1
2
3
4
5
6
7
if(!mStopped){
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}

MeasureSpec是一个32位的int值,前两位表示mode,后30位代表size。这个应该要展开来讲,不清楚的可以网上看看。

这里的performMeasure中传入的两个值就是最顶层的MeasureSpec。由 getRootMeasureSpec()方法获取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {

case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}

windowSize是窗口的尺寸,(1920*1080或者是2660**1440等,根据设备而定);

然后根据LayoutParams具体是什么来设置measureSpec。代码写的很清楚。

再往下走,看performMeasure()方法:

1
2
3
4
5
6
7
8
9
10
11
12
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//************熟悉的measure方法啊*************
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

关键就那一句,熟悉的measure方法开始了。
不要忘了mView是谁,没错,就是上文(ViewRootImpl.setView())中传来的view,上上文(windowManager.addView())中传来的view——mDecor。

如果你记忆力还比较好的话,你应该记得文章开头的地方列出了decor的继承关系——继承自ViewGroup。

下一篇:View/ViewGroup 的测量(measure )流程