Activity setContentView(int layoutResID) 发生了什么

本文详细解析了Android中Activity界面的加载流程,从setContentView方法入手,深入探讨了其内部实现机制,包括如何通过反射创建Window对象、LayoutInflater的工作原理及View的创建与填充过程。
摘要由CSDN通过智能技术生成
     在写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 context ActivityThread aThread ,
        Instrumentation instr IBinder token , int ident ,
        Application application Intent intent ActivityInfo info ,
        CharSequence title Activity parent String 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, mToken mComponent.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(layoutResID mContentParent) ;
    final Callback cb = getCallback() ;
    if (cb !=  null && !isDestroyed()) {
        //通知内容已经改变
        cb.onContentChanged() ;
    }
}

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

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

public View inflate(XmlPullParser parser ViewGroup 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(parser root attrs , false) ;
           else {
                // Temp is the root view that was found in the xml
                View temp //一直觉得这个命名比较奇怪,但是也是一种根节点的方式
                if (TAG_1995.equals(name)) {
                    temp =  new BlinkLayout(mContext attrs) ;
               else //正常节点  创建一个根view
                    temp = createViewFromTag(root name attrs) ;
                }

                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(parser temp attrs , 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(temp params) ;
                }

                // 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 parent String name AttributeSet 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(parent name mContext attrs) ;
       else if (mFactory !=  null) {
            view = mFactory.onCreateView(name mContext attrs) ;
       else {
            view =  null;
        }

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

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

                //这个方法 最终会补一个前缀调用 createView(name, "android.view.", attrs); 方法和下面走的是同一个方法 只不过是自己加了个前缀
                view = onCreateView(parent name attrs) ;
           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 parser View parent , final AttributeSet attrs ,
              boolean finishInflate)  throws XmlPullParserException IOException {

    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(parser parent) ;
       else if (TAG_INCLUDE.equals(name)) {
            if (parser.getDepth() ==  0) {  //include 节点不能未根元素
                throw new InflateException( "<include /> cannot be the root element") ;
            }
            parseInclude(parser parent attrs) ;
       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(mContext attrs) ;
            final ViewGroup viewGroup = (ViewGroup) parent ;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs) ;
            rInflate(parser view attrs , true) ;
            viewGroup.addView(view params) ;
       else {
            //走创建createViewFromTag方法
            final View view = createViewFromTag(parent name attrs) ;
            final ViewGroup viewGroup = (ViewGroup) parent ;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs) ;
            rInflate(parser view attrs , true) ;
            //给上一级添加子view
            viewGroup.addView(view params) ;
        }
    }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值