自定义view学习笔记(三)

本文详细剖析了Android Activity的构成,从setContentView开始,探讨了Window、PhoneWindow、DecorView以及View的工作流程。重点在于measure、layout和draw阶段,解释了MeasureSpec在测量过程中的作用,展示了如何将View加载到Window并最终呈现在屏幕上。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

view的工作流程:measure ,layout,draw
其中measure是用来测量view的宽和高, layout 用来确定view的位置,draw则用来绘制view。
(1)首先了解Activity的构成
首先会调用setContentView函数来加载布局,具体实现为

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

这里调用了getWindow().setContentView(ID),那么getWindow指的是什么?

public Window getWindow() {
    return mWindow;
}

mWindow是在Activity的attach函数中进行初始化的

final void attach(Context context, ActivityThread aThread,参数省略) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);

    mWindow = new PhoneWindow(this, window);

所以getWindow指的是PhoneWindow,继续查看PhoneWindow的setContentView

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.
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
.......
}

mContentParent 为ViewGroup类型,第一次会调用installDecor()函数

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);

会初始化mDecor 和 mContentParent,本次主要关注mContentParent
会调用generateDecor函数

protected DecorView generateDecor(int featureId) {
.......
        return new DecorView(context, featureId, this, getAttributes());
}

在这里会创建一个DecorView,这个DecorView就是Activity中的根View
generateLayout方法主要是根据情况加载不同的布局给layoutResource

上述创建了DecorView,但是它的内容还无法显示,因为还没有加载到Window中
当我们调用startActivity方法时,最终会调用ActivityThread的handleLaunchActivity方法来创建Activity的

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    Activity a = performLaunchActivity(r, customIntent); //1
    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); //2

注释1:调用performLaunchActivity方法来创建Activity,在这里会调用到Activity的onCreate方法,从而完成DecorView的创建
注释2:handleResumeActivity方法

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ActivityClientRecord r = mActivities.get(token); //1
      if (r != null) {
        final Activity a = r.activity;
           if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView(); //2
            decor.setVisibility(View.INVISIBLE); 
            ViewManager wm = a.getWindowManager(); //3
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient && !a.mWindowAdded) {
          a.mWindowAdded = true;
          wm.addView(decor, l); //4
}

注释2:得到了DecorView
注释3:得到了WindowManager,WindowManger是一个接口并且继承了ViewManager,它的实现类是WindowManagerImpl
注释4:调用了WindowManager的addView方法

查看WindowManagerImpl.addView方法

public final class WindowManagerImpl implements WindowManager {
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

又调用了WindowManagerGlobal的addView方法

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
....
ViewRootImpl root;
View panelParentView = null;
root = new ViewRootImpl(view.getContext(), display); //1
view.setLayoutParams(wparams);

mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);  //2

注释1:创建一个ViewRootImpl实例
注释2:调用了ViewRootImpl的setView方法并将DecorView作为参数传进去,这样就把DecoreView加载到Window中,但是界面仍然不会显示出来,因为view的工作流程还每执行完成 , 需要经过measure ,layout ,draw彩绘把view绘制出

是通过ViewRootImpl的performTraveals函数,这个方法使ViewTree开始view的工作流程
这里面主要执行了3个方法,分别是performMeasure、performLayout和performDraw,在其方法的内部又会分别调用View的measure、layout和draw方法。同时这里还会调用view的measure方法(进行测量)

performMeasure需要传入两个参数,需要了解MeasureSpec

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

(2)理解MeasureSpec
MeasureSpec是View 的内部类,其封装了View的规格尺寸,包括View的宽高,它的作用是在measure流程中,系统会将View的layoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后在onMeasure方法中根据这个MeasureSpec来确定View的宽高

MeasureSpec代表32为,其中前两位代表specMode,后30位代表SpecSize
SpecMode:表示测量模式
SpecSize : 表示测量大小

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值