欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

AndroidUI绘制流程及原理

程序员文章站 2024-03-24 09:59:10
...

一 布局加载

  setContentView(R.layout.layout);

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

AndroidUI绘制流程及原理
定位到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 AndroidUI绘制流程及原理
到这里 installDecor()这个方法就完成了,走到这里我们来梳理一下

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

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

AndroidUI绘制流程及原理

相关标签: android