美文网首页
View体系里面的相关知识

View体系里面的相关知识

作者: 慕尼黑凌晨四点 | 来源:发表于2020-10-08 22:41 被阅读0次

关于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)

mContentParentViewGroup对象

setContentView的执行

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

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

    关注后两个注释即可,一个初始化了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()方法

代码可抽象成这样

//不是源码!!!!!
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的。

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()方法,mGlobalwindowManagerGlobal对象,windowManagerGlobaladdView方法如下:

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。

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

相关文章:鸿洋 - 我感觉我学了一个假的Android...

相关文章

网友评论

      本文标题:View体系里面的相关知识

      本文链接:https://www.haomeiwen.com/subject/okkkpktx.html