AndroidUI绘制流程及原理

一 布局加载

  setContentView(R.layout.layout);

我们平时写好Layout布局,只需调用 setContentView(R.layout.layout);这个方法就可以把写的页面显示在屏幕上,那么这个方法背后到底做了些什么了?我们看看源码,定位到 setContentView这个方法的源码

在这里插入图片描述
定位到ActivitysetContentView方法中

getWindow().setContentView(layoutResID);

通过调用 getWindow().setContentView(layoutResID);这个方法将我们的布局ID传了进去

我们看看 getWindow() 得到了什么?

public Window getWindow() {
    return mWindow;
}

它返回的是一个mWindow对象,那么mWindow又是什么了?

* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
大概意思 PhoneWindow 是 Window 的唯一子类

cpp
public abstract class Window {
    /** Flag for the "options panel" feature.  This is enabled by default. */
    public static final int FEATURE_OPTIONS_PANEL = 0;
    /** Flag for the "no title" feature, turning off the title at the top
     *  of the screen. */
    public static final int FEATURE_NO_TITLE = 1;

可以看到 mWindow是一个抽象类的对象,而 PhoneWindow 是它的子类,所以通过源码我们知道 mWindow实际上就是 PhoneWindow 的一个对象而已

那么 getWindow().setContentView(layoutResID);实际上就是调用 PhoneWindow 里面的 setContentView(layoutResID)

我们来到PhoneWindow 类里面的 setContentView(int layoutResID) 方法,看看它做了什么?

@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.
    if (mContentParent == null) {
        installDecor();
    } 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.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

首先会执行 installDecor(); 这个方法

 private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {//判断 DecorView 是否为空 为空的话 就调用  generateDecor(-1) 创建一个
    			//DecorView  其实是我们界面窗口的 顶层容器 ,下面会有介绍及插图
        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);//  重点二

    // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
    mDecor.makeOptionalFitsSystemWindows();

定位到generateDecor(-1);方法里面

protected DecorView generateDecor(int featureId) {
    // System process doesn't have application context and in that case we need to directly use
    // the context we have. Otherwise we want the application context, so we don't cling to the
    // activity.
    Context context;
    if (mUseDecorContext) {
        Context applicationContext = getContext().getApplicationContext();
        if (applicationContext == null) {
            context = getContext();
        } else {
            context = new DecorContext(applicationContext, getContext());
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

我们可以看到 它是直接 new DecorView 就返回回去了

我们又回到 installDecor();方法中的 mContentParent = generateLayout(mDecor);这行代码

protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.//应用当前主题的数据

    TypedArray a = getWindowStyle();

    if (false) {
        System.out.println("From style:");
        String s = "Attrs:";
        for (int i = 0; i < R.styleable.Window.length; i++) {
            s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
                    + a.getString(i);
        }
        System.out.println(s);
    }
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
        & (~getForcedWindowFlags());
if (mIsFloating) {
    setLayout(WRAP_CONTENT, WRAP_CONTENT);
    setFlags(0, flagsToUpdate);
} else {
    setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}

if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
    requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
    // Don't allow an action bar if there is no title.
    requestFeature(FEATURE_ACTION_BAR);
}

if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
    requestFeature(FEATURE_ACTION_BAR_OVERLAY);
}

if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
    requestFeature(FEATURE_ACTION_MODE_OVERLAY);
}

if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
    requestFeature(FEATURE_SWIPE_TO_DISMISS);
}

if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
    setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}

if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
        false)) {
    setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
            & (~getForcedWindowFlags()));
}

if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
        false)) {
    setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
            & (~getForcedWindowFlags()));
}


int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
    layoutResource = R.layout.screen_swipe_dismiss;
    setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
    if (mIsFloating) {
        TypedValue res = new TypedValue();
        getContext().getTheme().resolveAttribute(
                R.attr.dialogTitleIconsDecorLayout, res, true);
        layoutResource = res.resourceId;
    } else {
        layoutResource = R.layout.screen_title_icons;
    }
    // XXX Remove this once action bar supports these features.
    removeFeature(FEATURE_ACTION_BAR);
    // System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) !=。。。 0
        && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
    // Special case for a window with only a progress bar (and title).
    // XXX Need to have a no-title version of embedded windows.
    layoutResource = R.layout.screen_progress;
    // System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) 
.........
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//重点代码一

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //重点代码二
if (contentParent == null) {
    throw new RuntimeException("Window couldn't find content container view");
}

........................
 以上代码的大概意思就是 设置主题属性 及 得到一个 系统的 初始化容器 layoutResource = R.layout.screen_title_icons;

当上面的一些主题及属性及相对应的 初始化容器 layoutResource = R.layout.screen_title_icons完成后;
就会执行这行代码

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//重点代码一


void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {//把得到的layoutResource 就是上面我们得到的系统Layout 传进去
    if (mBackdropFrameRenderer != null) {
        loadBackgroundDrawablesIfNeeded();
        mBackdropFrameRenderer.onResourcesLoaded(
                this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                getCurrentColor(mNavigationColorViewState));
    }

    mDecorCaptionView = createDecorCaptionView(inflater);
    final View root = inflater.inflate(layoutResource, null);//解析成一个view
    if (mDecorCaptionView != null) {
        if (mDecorCaptionView.getParent() == null) {
            addView(mDecorCaptionView,
                    new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mDecorCaptionView.addView(root,
                new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
    } else {

        // Put it below the color views.
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//然后添加到DecorView上面
    }
    mContentRoot = (ViewGroup) root;
    initializeElevation();
}

执行

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //重点代码二

根据源码我们可以知道 上面的 layoutResource(screen_simple.xml)它其实是一个线性布局而已

findViewById 就是得到这个线性布局里面的一个子容器(下图FrameLayout就是这个子容器)

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//FrameLayout 这个容器就是 我们得到的 contentParent 在这里插入图片描述
到这里 installDecor()这个方法就完成了,走到这里我们来梳理一下

第一步 :创建 DecorView 顶层容器
第二步: 向DecorView 里面添加一个系统的布局容器 (就是screen_simple,xml 它是一个线性布局)
第三步: 得到系统布局容器里面的 子容器 (FrameLayout)
第四步: 子容器(FrameLayout)就可以添加我们自己的 xml布局了

installDecor()方法中的 mContentParent 就是 子容器

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值