• Android视图载入到窗体的过程分析


    上一篇博客Android中Handler原理在讲到Handler的时候谈到了android的Activity启动是怎样运行到onCreate方法的,这篇主要从onCreate方法里面我们必需要写的方法setContentView開始,研究布局视图是怎样载入到手机窗体上的。

    当在运行到setContentView时,实际上运行的是

    public void setContentView(int layoutResID) {
            getWindow().setContentView(layoutResID);
            initActionBar();
    }

    能够看到实际上是Window类的setContentView方法

    private Window mWindow;
    public Window getWindow() {
            return mWindow;
    }

    Window类是一个抽象类,以下主要是找到他的实现类。mWindow初始化在

    final void attach(……) {
            ……
            mWindow = PolicyManager.makeNewWindow(this);
            mWindow.setCallback(this);
            mWindow.getLayoutInflater().setPrivateFactory(this);
    	……
        }

    Attach方法在main方法中。详细查看上一篇博客。调用了PolicyManager类的静态方法makeNewWindow生成Window对象

    private static final String POLICY_IMPL_CLASS_NAME =
            "com.android.internal.policy.impl.Policy";
    private static final IPolicy sPolicy;
    static {
            try {
                Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
                sPolicy = (IPolicy)policyClass.newInstance();
            }
    ……
        }
    public static Window makeNewWindow(Context context) {
            return sPolicy.makeNewWindow(context);
    }

    能够看到是通过Policy类的makeNewWindow方法得到的Window对象。这里是通过反射机制获取的Policy的实例。

    public Window makeNewWindow(Context context) {
            return new PhoneWindow(context);
    }

    能够看到实际上是一个PhoneWindow。那么依据多态。事实上在上面调用的就是PhoneWindow#setContentWindow

    public void setContentView(int layoutResID) {
            if (mContentParent == null) {
                installDecor();//1
            } else {
                mContentParent.removeAllViews();
            }
            mLayoutInflater.inflate(layoutResID, mContentParent);//2 将layoutResID填充。他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor);得到的
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
    }

    运行到installDecor()

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();//生成装饰窗体。装饰窗体继承自FrameLayout
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);// 产生布局。返回父布局,临时这样理解,详细进去看代码
            mDecor.makeOptionalFitsSystemWindows();
            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
            ......
            }
        }
    }

    generateLayout的代码例如以下

    protected ViewGroup generateLayout(DecorView decor) {
            // Apply data from current theme.
    
            TypedArray a = getWindowStyle();// 获取当前设置的主题
    		......
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
                requestFeature(FEATURE_NO_TITLE);//能够看到平时在AndroidManifest配置的窗体等各事实上在代码里都是在这里改动的
            } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
                // Don't allow an action bar if there is no title.
                requestFeature(FEATURE_ACTION_BAR);
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
                requestFeature(FEATURE_ACTION_BAR_OVERLAY);
            }
    	......
    	//19-63行依据我们指定的有无标题等各种窗体风格得到相应的默认布局。
    	//这些布局在D:SoftWareJavaandroid4.2-sourceframeworksasecore
    es
    eslayout
            int layoutResource;
            int features = getLocalFeatures();
            if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = com.android.internal.R.layout.screen_title_icons;
                }
                removeFeature(FEATURE_ACTION_BAR);
            } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                    && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
                layoutResource = com.android.internal.R.layout.screen_progress;
            } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = com.android.internal.R.layout.screen_custom_title;
                }
                removeFeature(FEATURE_ACTION_BAR);
            } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                    if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
                        layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
                    } else {
                        layoutResource = com.android.internal.R.layout.screen_action_bar;
                    }
                } else {
                    layoutResource = com.android.internal.R.layout.screen_title;
                }
            } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
                layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
            } else {
                layoutResource = com.android.internal.R.layout.screen_simple;
            }
    
            mDecor.startChanging();
    
            View in = mLayoutInflater.inflate(layoutResource, null);//依据上面的推断选择的layoutResource填充成View
            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//调用装饰窗体的addView方法将上一步生成的View加入到最外层的装饰窗体
    	
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT相应的都是@android:id/content事实上是一个FrameLayout
            ......
            mDecor.finishChanging();
            return contentParent;
        }

    以下是最经常使用的layoutResource布局文件他们在D:SoftWareJavaandroid4.2-sourceframeworksasecore es eslayout目录下

    Screen-title.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:fitsSystemWindows="true">
        <!-- Popout bar for action modes -->
        <ViewStub android:id="@+id/action_mode_bar_stub"
                  android:inflatedId="@+id/action_mode_bar"
                  android:layout="@layout/action_mode_bar"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content" />
        <FrameLayout
            android:layout_width="match_parent" 
            android:layout_height="?android:attr/windowTitleSize"
            style="?android:attr/windowTitleBackgroundStyle">
            <TextView android:id="@android:id/title" 
                style="?

    android:attr/windowTitleStyle" android:background="@null" android:fadingEdge="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /> </LinearLayout>

    最外层是线性布局。包括帧布局(包括一个TextView事实上就是标题)和帧布局。事实上这个就是最常见的样子。

    默认生成的程序就是这个样子。

    Screen-simple.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
        <ViewStub android:id="@+id/action_mode_bar_stub"
                  android:inflatedId="@+id/action_mode_bar"
                  android:layout="@layout/action_mode_bar"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content" />
        <FrameLayout
             android:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:foregroundInsidePadding="false"
             android:foregroundGravity="fill_horizontal|top"
             android:foreground="?android:attr/windowContentOverlay" />
    </LinearLayout>

    显而易见这个就是全屏设置时默认载入的布局。

    我们能够看到id为content的FrameLayout都存在的,由于他要装载我们填充的xml

    依Screen-title.xml为例。大致是这样子的


    通过以上分析明确了下面几点:

    1. Activity呈现出来的界面事实上是一个PhoneWindow类在管理的。这个类中有一个DecorView成员就是最外层的一个容器。

    2. 上面这张图是很重要的,显示了窗体的结构。

    3. Activity究竟是个什么东西?还真不好说清楚…^_^总之和刚開始的认识是不同的。

    4. 遇到不懂得问题就去查看源代码。代码是最好的老师!









  • 相关阅读:
    内存映射文件点滴
    [Buzz.Today]“估摸”手机:Google收购摩托罗拉
    QT程序中的事件处理
    又是一个开始
    HTTP 协议基础
    深入理解HTTP消息头
    HTTP请求模型和头信息
    十个让你变成糟糕的程序员的行为
    获取上层调用函数地址的代码
    MIME类型大全
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/7043494.html
Copyright © 2020-2023  润新知