zoukankan      html  css  js  c++  java
  • 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. 遇到不懂得问题就去查看源代码。代码是最好的老师!









  • 相关阅读:
    java 在线网络考试系统源码 springboot mybaits vue.js 前后分离跨域
    springboot 整合flowable 项目源码 mybiats vue.js 前后分离 跨域
    flowable Springboot vue.js 前后分离 跨域 有代码生成器 工作流
    Flowable 工作流 Springboot vue.js 前后分离 跨域 有代码生成器
    java 企业 网站源码 后台 springmvc SSM 前台 静态化 代码生成器
    java 进销存 商户管理 系统 管理 库存管理 销售报表springmvc SSM项目
    基于FPGA的电子计算器设计(中)
    基于FPGA的电子计算器设计(上)
    FPGA零基础学习:SPI 协议驱动设计
    Signal tap 逻辑分析仪使用教程
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/7043494.html
Copyright © 2011-2022 走看看