Activity setContentView(int layoutResID) 发生了什么

来源:互联网 发布:苍南哪有淘宝培训 编辑:程序博客网 时间:2024/05/22 11:45
     在写activity时,一般用setContentView 来设置界面。
public void setContentView(int layoutResID) {
    getWindow().setContentView(layoutResID);
    initActionBar();
}

/**
 * Retrieve the current {@link android.view.Window} for the activity.
 * This can be used to directly access parts of the Window API that
 * are not available through Activity/Screen.
 * 
 * @return Window The current window, or null if the activity is not
 *         visual.
 */
public Window getWindow() {
    return mWindow;
}

获取当前activity的 window对象,看一下在哪里给mWindow赋值的。

final void attach(Context contextActivityThread aThread,
        Instrumentation instrIBinder token, int ident,
        Application applicationIntent intentActivityInfo info,
        CharSequence titleActivity parentString id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config) {
    attachBaseContext(context);

    mFragments.attachActivity(this);

    //给当前activity赋值 window
    mWindow = PolicyManager.makeNewWindow(this);
    //设置回调 就是常用的各种事件,键盘之类的回调
    mWindow.setCallback(this);
    //设置设置一个自定义的布局填充方法 后面会看到
    mWindow.getLayoutInflater().setPrivateFactory(this);
    //键盘输入
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();
   
    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;

    mWindow.setWindowManager(null, mTokenmComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;
}

attach 这个是在activity 被反射 构建实例的之后调用这个方法。
看 PolicyManager这个类

public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";

    private static final IPolicy sPolicy;

    static {
        // Pull in the actual implementation of the policy at run-time
        try {
            //这里通过反射 实例化 Policy 类
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
       catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be loaded"ex);
       catch (InstantiationException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated"ex);
       catch (IllegalAccessException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated"ex);
        }
    }

    // Cannot instantiate this class
    private PolicyManager() {}

    // The static methods to spawn new policy-specific objects
    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

    public static LayoutInflater makeNewLayoutInflater(Context context) {
        return sPolicy.makeNewLayoutInflater(context);
    }

    public static WindowManagerPolicy makeNewWindowManager() {
        return sPolicy.makeNewWindowManager();
    }

    public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return sPolicy.makeNewFallbackEventHandler(context);
    }
}


Policy 构建 PhoneWindow对象 

//构建的是PhoneWindow对象
public Window makeNewWindow(Context context) {
    return new PhoneWindow(context);
}

PhoneWindow 的 setContentView 方法
@Override
public void setContentView(int layoutResID) {
    //设置界面布局,初始化根容器
    if (mContentParent == null) {
        installDecor() //会初始化跟布局并设置顶部导航
   else {
        //设置view时,会首先将以后的view 移除掉
        mContentParent.removeAllViews();
    }
    //同样使用LayoutInflater 填充布局
    mLayoutInflater.inflate(layoutResIDmContentParent);
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        //通知内容已经改变
        cb.onContentChanged();
    }
}

接着看 LayoutInflater 是怎么填充布局的

public View inflate(int resourceViewGroup root, boolean attachToRoot) {
    if (DEBUG) System.out.println("INFLATING from resource: " + resource);
    XmlResourceParser parser = getContext().getResources().getLayout(resource);
    try {
        return inflate(parserrootattachToRoot);
   finally {
        parser.close();
    }
}

public View inflate(XmlPullParser parserViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = mContext;
        View result = root;

        try {
            // Look for the root node.
            //找到根节点,开始节点
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {
                // Empty
            }

            //如果没有开始节点,认为xml错误 抛出异常
            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }

            //获取跟节点名称
            final String name = parser.getName();

            if (DEBUG) {
                System.out.println("**************************");
                System.out.println("Creating root view: "
                        + name);
                System.out.println("**************************");
            }

            //merge的填充方式
            if (TAG_MERGE.equals(name)) {
                if (root == null || !attachToRoot) {
                    throw new InflateException("<merge /> can be used only with a valid "
                           "ViewGroup root and attachToRoot=true");
                }

                rInflate(parserrootattrs, false);
           else {
                // Temp is the root view that was found in the xml
                View temp//一直觉得这个命名比较奇怪,但是也是一种根节点的方式
                if (TAG_1995.equals(name)) {
                    temp = new BlinkLayout(mContextattrs);
               else //正常节点  创建一个根view
                    temp = createViewFromTag(rootnameattrs);
                }

                ViewGroup.LayoutParams params = null;

                if (root != null) {
                    if (DEBUG) {
                        System.out.println("Creating params from root: " +
                                root);
                    }
                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                    }
                }

                if (DEBUG) {
                    System.out.println("-----> start inflating children");
                }
                // Inflate all children under temp
                //填充所有的子控件
                rInflate(parsertempattrs, true);
                if (DEBUG) {
                    System.out.println("-----> done inflating children");
                }

                // We are supposed to attach all the views we found (int temp)
                // to root. Do that now.
                if (root != null && attachToRoot) {
                    root.addView(tempparams);
                }

                // Decide whether to return the root that was passed in or the
                // top view found in xml.
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }

        } catch (XmlPullParserException e) {
            InflateException ex = new InflateException(e.getMessage());
            ex.initCause(e);
            throw ex;
       catch (IOException e) {
            InflateException ex = new InflateException(
                    parser.getPositionDescription()
                            + ": " + e.getMessage());
            ex.initCause(e);
            throw ex;
       finally {
            // Don't retain static reference on context.
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;
        }

        return result;
    }
}

View createViewFromTag(View parentString nameAttributeSet attrs) {
    if (name.equals("view")) { //获取没有命名空间的view 名字
        name = attrs.getAttributeValue(null, "class");
    }

    if (DEBUG) System.out.println("******** Creating view: " + name);

    try {
        View view;
        //这个mFractory 都是可以被设置成自定义的实现方式,
        // 他继承Factory这个类需要实现onCreateView方法 返回一个view,可以看作是一个暴露出去自定义的一种实现方式
        if (mFactory2 != null) {
            view = mFactory2.onCreateView(parentnamemContextattrs);
       else if (mFactory != null) {
            view = mFactory.onCreateView(namemContextattrs);
       else {
            view = null;
        }

        //和 Factory一样  只是参数不哟样
        if (view == null && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parentnamemContextattrs);
        }

        if (view == null) {     //如果这时候 view 已然为null 说明没有自定义实现  需要自己来做
            //这里如果不包含点 说明是系统级别的返回,如果包含说明是自定义的 ,用不用的命名空间,就是没有前缀
            if (-== name.indexOf('.')) {

                //这个方法 最终会补一个前缀调用 createView(name, "android.view.", attrs); 方法和下面走的是同一个方法 只不过是自己加了个前缀
                view = onCreateView(parentnameattrs);
           else {
                view = createView(name, null, attrs);
            }
        }

        if (DEBUG) System.out.println("Created view is: " + view);
        return view;

   catch (InflateException e) {
        throw e;

   catch (ClassNotFoundException e) {
        InflateException ie = new InflateException(attrs.getPositionDescription()
                + ": Error inflating class " + name);
        ie.initCause(e);
        throw ie;

   catch (Exception e) {
        InflateException ie = new InflateException(attrs.getPositionDescription()
                + ": Error inflating class " + name);
        ie.initCause(e);
        throw ie;
    }
}


 */
//采用递归的方式将所有自节点填充完毕
void rInflate(XmlPullParser parserView parent, final AttributeSet attrs,
              boolean finishInflate) throws XmlPullParserExceptionIOException {

    final int depth = parser.getDepth();
    int type;

    //循环填充
    while (((type = parser.next()) != XmlPullParser.END_TAG ||
            parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

        if (type != XmlPullParser.START_TAG) {
            continue;
        }

        final String name = parser.getName();

        //根据不同类型的节点 采用不用的填充方式
        if (TAG_REQUEST_FOCUS.equals(name)) {
            parseRequestFocus(parserparent);
       else if (TAG_INCLUDE.equals(name)) {
            if (parser.getDepth() == 0) { //include 节点不能未根元素
                throw new InflateException("<include /> cannot be the root element");
            }
            parseInclude(parserparentattrs);
       else if (TAG_MERGE.equals(name)) { //merge 节点必须为根节点
            throw new InflateException("<merge /> must be the root element");
       else if (TAG_1995.equals(name)) {
            final View view = new BlinkLayout(mContextattrs);
            final ViewGroup viewGroup = (ViewGroup) parent;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
            rInflate(parserviewattrs, true);
            viewGroup.addView(viewparams);
       else {
            //走创建createViewFromTag方法
            final View view = createViewFromTag(parentnameattrs);
            final ViewGroup viewGroup = (ViewGroup) parent;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
            rInflate(parserviewattrs, true);
            //给上一级添加子view
            viewGroup.addView(viewparams);
        }
    }

    //布局填充结束  会调用次方法
    if (finishInflate) parent.onFinishInflate();
}

这样 一个布局就填充结束了 ,这个还是比较简单的,代码逻辑也不复杂,但是view的绘制流程,和事件分发传递过程 就比较复杂了。 
0 0