zoukankan      html  css  js  c++  java
  • Android Activity之 setContentView()总结

    从一开始hello world的第一个安卓应用开始,Activity 自动生成,布局自动生成,直接修改布局,在Activity中,findviewById()找到view,然后处理相应的业务逻辑即可,那么setContentView(),是怎么工作的?

    ~~进入Activity的setContentView源码:

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

      

    可以看到里面获取了Window,然后调用了Window的setContentView。

    这里的Window的实现类是PhoneWindow(package com.android.internal.policy.impl;),我们直接看它的实现:

     @Override
        public void setContentView(int layoutResID) {
            if (mContentParent == null) {
                installDecor();
            } else {
                mContentParent.removeAllViews();
            }
            mLayoutInflater.inflate(layoutResID, mContentParent);
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    

    可以看到,首先判断mContentParent是否为null,是则调用installDecor(),否则移除其内部所有的子Views,然后通过LayoutInflater.inflate将我们传入的layout放置到mContentParent中。

    从这里就能看出来mContentParent是个ViewGroup且包裹我们整个布局文件;而installDecor()估计就是去初始化我们这个mContentParent,一会我们会去验证。

    接下来,通过getCallBack拿到了一个CallBack对象,其实这个获取到的这个CallBack就是我们Activity自己,你可以去看我们的Activity是实现了CallBack接口的。

    这个Callback明显就是一个回调,当PhoneWindow接收到系统分发给它的触摸、IO、菜单等相关的事件时,可以回调相应的Activity进行处理。至于Callback可以回调哪些方法,自己看下这个接口的声明方法即可。当然了这里不是我们的关键,因为我们的setContentView里面只是回调了onContentChanged,而onContentChanged在Activity中是空实现。

    好了,接下来去看我们的installDecor()

    private void installDecor() {
    	        if (mDecor == null) {
    	            mDecor = generateDecor();
    	            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    	            mDecor.setIsRootNamespace(true);
    	            //...
    	            }
    	        }
    	        if (mContentParent == null) {
    	            mContentParent = generateLayout(mDecor);
    	            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
    	            if (mTitleView != null) {
    	               //根据FEATURE_NO_TITLE隐藏,或者设置mTitleView的值
    	            	//...
    	            } else {
    	                mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
    	                if (mActionBar != null) {
    	                	//设置ActionBar标题、图标神马的;根据FEATURE初始化Actionbar的一些显示
    	                	//...
    	                }
    	            }
    	        }
    	}
    

      

    这里代码比较长,删除了一些初始化Actionbar样式神马的代码。

    可以看到这里不仅初始化mContentParent,而且在之前先调用generateDecor();初始化了一个mDecor,mDecor是DecorView对象,为FrameLayout的子类。

    在得到mDecor以后设置其焦点的获取方式为,当其子孙都不需要时,自己才获取。

    然后通过 generateLayout(mDecor);把mDecor做为参数传入,然后获取到了我们的mContentParent;

    接下里就开始通过findViewById进行获取控件了,而这里的findViewById的代码是这样的:

     public View findViewById(int id) {
            return getDecorView().findViewById(id);
        }
    

      

    getDecorView返回的就是我们的mDecor。

    这里我们猜测下,首先去初始化mDecor,然后通过mDecor初始化了mContentParent,接下来mDecor就可以使用findViewById方法了。那么我觉得,在初始化mDecor的方法

    generateDecor()中,一定为我们的mDecor放入了布局或者控件(最简单的就是使用inflate压入了布局文件),而mContentParent可能就是mDecor中的某个子View。

    是不是这样呢?

    我们一起来先看看generateDecor()方法的实现:

    protected DecorView generateDecor() {     

        return new DecorView(getContext(), -1);   

      }  

    public DecorView(Context context, int featureId) {  

              super(context);  

             mFeatureId = featureId;  }

     

    很遗憾,我们的generateDecor()只是初始化了一个FrameLayout对象,并没有在其内部压入布局文件,看来我们的猜测有些问题;不过没事,既然此方法没有,那么generateLayout(mDecor);中一定设置了layout文件,并且这名字也很像这么回事。

    protected ViewGroup generateLayout(DecorView decor) {
    			        // Apply data from current theme.
    			        TypedArray a = getWindowStyle();
    			        //...Window_windowIsFloating,Window_windowNoTitle,Window_windowActionBar...
    			        //首先通过WindowStyle中设置的各种属性,对Window进行requestFeature或者setFlags
    			
    			        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
    			            requestFeature(FEATURE_NO_TITLE);
    			        }
    			        //...
    			        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
    			            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
    			        }
    			        //...根据当前sdk的版本确定是否需要menukey
    			        WindowManager.LayoutParams params = getAttributes();
    			        //通过a中设置的属性,设置  params.softInputMode 软键盘的模式;
    			        //如果当前是浮动Activity,在params中设置FLAG_DIM_BEHIND并记录dimAmount的值。
    			        //以及在params.windowAnimations记录WindowAnimationStyle
    			        
    			        // Inflate the window decor.
    			        int layoutResource;
    			        int features = getLocalFeatures();
    			        // System.out.println("Features: 0x" + Integer.toHexString(features));
    			        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;
    			            }
    			            // 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 = com.android.internal.R.layout.screen_progress;
    			            // System.out.println("Progress!");
    			        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
    			            // Special case for a window with a custom title.
    			            // If the window is floating, we need a dialog layout
    			            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;
    			            }
    			            // XXX Remove this once action bar supports these features.
    			            removeFeature(FEATURE_ACTION_BAR);
    			        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
    			            // If no other features and not embedded, only need a title.
    			            // If the window is floating, we need a dialog layout
    			            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) {
    			                layoutResource = com.android.internal.R.layout.screen_action_bar;
    			            } else {
    			                layoutResource = com.android.internal.R.layout.screen_title;
    			            }
    			            // System.out.println("Title!");
    			        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
    			            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
    			        } else {
    			            // Embedded, so no decoration is needed.
    			            layoutResource = com.android.internal.R.layout.screen_simple;
    			            // System.out.println("Simple!");
    			        }
    			
    			
    			        View in = mLayoutInflater.inflate(layoutResource, null);
    			        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    			
    			        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    			        //...
    			
    			        return contentParent;
    			    }
    	     }
    

      

      

    代码也比较长,首先getWindowStyle在当前的Window的theme中获取我们的Window中定义的属性。具体参考:frameworksasecore es esvaluesattrs.xml

     <!-- The set of attributes that describe a Windows's theme. -->
        <declare-styleable name="Window">
            <attr name="windowBackground" />
            <attr name="windowContentOverlay" />
            <attr name="windowFrame" />
            <attr name="windowNoTitle" />
            <attr name="windowFullscreen" />
            <attr name="windowOverscan" />
            <attr name="windowIsFloating" />
            <attr name="windowIsTranslucent" />
            <attr name="windowShowWallpaper" />
            <attr name="windowAnimationStyle" />
            <attr name="windowSoftInputMode" />
            <attr name="windowDisablePreview" />
            <attr name="windowNoDisplay" />
            <attr name="textColor" />
            <attr name="backgroundDimEnabled" />
            <attr name="backgroundDimAmount" />
    

      

    然后就根据这些属性的值,对我们的Window各种requestFeature,setFlags等等。所以这里就是解析我们为Activity设置theme的地方,至于theme一般可以在AndroidManifest里面进行设置。

    接下来就到关键的部分了,21-75行:通过对features和mIsFloating的判断,为layoutResource进行赋值,至于值可以为R.layout.screen_custom_title;R.layout.screen_action_bar;等等。至于features,除了theme中设置的,我们也可以在Activity的onCreate的setContentView之前进行requestFeature,也解释了,为什么需要在setContentView前调用requestFeature设置全屏什么的。

    得到了layoutResource以后,78行,通过LayoutInflater把布局转化成view,加入到我们的decor,即传入的mDecor中。

    接下来81行:通过mDecor.findViewById传入R.id.content(相信这个id大家或多或少都听说过),返回mDecor(布局)中的id为content的View,一般为FrameLayout。

    好了,可以看到我们的mDecor是一个FrameLayout,然后会根据theme去选择系统中的布局文件,将布局文件通过inflate转化为view,加入到mDecor中;这些布局文件中都包含一个id为content的FrameLayout,将其引用返回给mContentParent。

    等我们的mContentParent有值了以后,返回之前PhoneWindow的setContentView的源码继续查看:

    有了mContentParent,然后把我们写的布局文件通过inflater加入到mContentParent中。

    关于R.layout.xxx可以在frameworksasecore es eslayout里面进行查看。

    例如:R.layout.screen_custom_title.xml

    <?xml version="1.0" encoding="utf-8"?>
    
    <!--
    This is a custom layout for a screen.
    -->
    
    <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:id="@android:id/title_container" 
            android:layout_width="match_parent" 
            android:layout_height="?android:attr/windowTitleSize"
            style="?android:attr/windowTitleBackgroundStyle">
        </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>
    

      

    上面的title_container是用来放自定义Title的容器,而下面的content就是放置我们设置的布局的容器。关于自定义Title例子,大家可以百度下。

    到此,我们的setContentView就分析完成了,我们可以回顾一下:

    首先初始化mDecor,即DecorView为FrameLayout的子类。就是我们整个窗口的根视图了。

    然后,根据theme中的属性值,选择合适的布局,通过infalter.inflater放入到我们的mDecor中。

    在这些布局中,一般会包含ActionBar,Title,和一个id为content的FrameLayout。

    最后,我们在Activity中设置的布局,会通过infalter.inflater压入到我们的id为content的FrameLayout中去。

    以上转自:http://blog.csdn.net/lmj623565791/article/details/41894125

    从当前的activity获得根视图方法:

    getWindow().getDecorView().findViewById(android.R.id.content)

       getWindow().getDecorView()就是根视图了,当然用Activity.getWindow.getDecorView()来表达意思更为明确。

    这里要主要说一下,android.R.id.content,这个ID对应的是什么鬼,这个就是根视图里用来盛放setContentView传入的布局的容器,它是一个线性布局,

    (ViewGroup) findViewById(android.R.id.content)可以获得挂在一个activity内容部分LinearLayout。

    上图:

    这样基本就清楚了,那么官方给出的对应demo的代码,看起来也就不那么奇怪/* * Copyright (C) 2012 The Android Open Source Project

     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.example.android.ui;
    
    import com.example.android.displayingbitmaps.BuildConfig;
    import com.example.android.util.Utils;
    
    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;
    import android.support.v4.app.FragmentTransaction;
    
    
    /**
     * Simple FragmentActivity to hold the main {@link ImageGridFragment} and not much else.
     */
    public class ImageGridActivity extends FragmentActivity {
        private static final String TAG = "ImageGridActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            if (BuildConfig.DEBUG) {
                Utils.enableStrictMode();
            }
            super.onCreate(savedInstanceState);
    
            if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
                final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                ft.add(android.R.id.content, new ImageGridFragment(), TAG);
    //不要企图去你自己工程去找上面ID,是系统的
    ft.commit(); } } }

      

     
  • 相关阅读:
    HDU 5213 分块 容斥
    HDU 2298 三分
    HDU 5144 三分
    HDU 5145 分块 莫队
    HDU 3938 并查集
    HDU 3926 并查集 图同构简单判断 STL
    POJ 2431 优先队列
    HDU 1811 拓扑排序 并查集
    HDU 2685 GCD推导
    HDU 4496 并查集 逆向思维
  • 原文地址:https://www.cnblogs.com/spring87/p/4910846.html
Copyright © 2011-2022 走看看