关于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()
源码如下:@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; }
关注后两个注释即可,一个初始化了
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()
方法
代码可抽象成这样:
//不是源码!!!!!
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的clickable
或LongClickable
为true
且 ACTION_UP
,则调用 performClick()
方法;
performClick()
方法中对onCLickListener
进行了响应:mListenerInfo.mOnClickListener.onClick(this)
图示

对了,默认情况下ViewGroup.onInterceptTouchEvent()
返回false,即默认可以一条路走下来。
View的工作流程
当我们调用Activity的startActivity()
方法时,最终是调用ActivityThread.handleLaunchActivity
方法 来创建Activity的。
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);
...
}
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
中:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
跟着又用到了mGlobal.addView()
方法,mGlobal
是windowManagerGlobal
对象,windowManagerGlobal
中addView
方法如下:
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
中,有如下代码:
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()
方法获取:
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
是窗口的尺寸,(19201080或者是2660*1440等,根据设备而定);
然后根据LayoutParams
具体是什么来设置measureSpec
。代码写的很清楚。
再往下走,看performMeasure()
方法:
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。
网友评论