Activity中setContentView过程

Activity中setContentView过程Windows概念Android手机中所有的视图都是通过Window来呈现的,像常用的Activity,Dialog,PopupWindow,Toast,他们的视图都是附加在Window上的,所以可以这么说——Window是View的直接管理者。Activity中加载布局Activity中加载布局,都是通过在onCreate中调用setContentView方法开始:

大家好,又见面了,我是你们的朋友全栈君。

Windows概念

  Android手机中所有的视图都是通过Window来呈现的,像常用的Activity,Dialog,PopupWindow,Toast,他们的视图都是附加在Window上的,所以可以这么说 ——Window是View的直接管理者。

Activity中加载布局

Activity中加载布局,都是通过在onCreate中调用setContentView方法开始:

@Override  
protected void onCreate(Bundle savedInstanceState) {   
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main_layout);  
    ButterKnife.bind(this);  
}  

Activity中有setContentView重载方法,一个是layoutid,一个直接传入view:

public void setContentView(@LayoutRes int layoutResID) {  
       getWindow().setContentView(layoutResID);  
       initWindowDecorActionBar();  
   }  
public void setContentView(View view) {  
       getWindow().setContentView(view);  
       initWindowDecorActionBar();  
   }  
public void addContentView(View view, ViewGroup.LayoutParams params) {
       getWindow().addContentView(view, params);
       initWindowDecorActionBar();
}

实际上调用的都是PhoneWindow的setContentView方法(PhoneWindow是Window的具体实现类)。initWindowDecorActionBar一看就知道是初始化actionbar的 :

@Override  
public void setContentView(int layoutResID) {  
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window 
    // decor, when theme attributes and the like are crystalized. Do not check the feature 
    // before this happens. 
    if (mContentParent == null) {  
        //如果mContentParent为空调用installDecor,初始化
        installDecor();  
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
        //如果内容已经加载过(不是第一次加载),并且不需要动画,removeAllViews()
        mContentParent.removeAllViews();  
    }  

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,  getContext());  
        //添加完Content后如有设置了FEATURE_CONTENT_TRANSITIONS则添加Scene来过度启动
        transitionTo(newScene);  
    } else {              
        //可以看到mContentParent这个view作为layoutResID的parent,所以layoutResID根width/height参数有效 
        mLayoutInflater.inflate(layoutResID, mContentParent);  
    }  
    mContentParent.requestApplyInsets();  
    final Callback cb = getCallback();  
    if (cb != null && !isDestroyed()) {  
        cb.onContentChanged();  
    }  
}  

@Override  
public void setContentView(View view) {  
    //所以始终是MATCH_PARENT,所以该view的width/height无效 
    setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}  

@Override  
public void setContentView(View view, ViewGroup.LayoutParams params) {  
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window 
    // decor, when theme attributes and the like are crystalized. Do not check the feature 
    // before this happens. 
    if (mContentParent == null) {  
        installDecor();  
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
        mContentParent.removeAllViews();  
    }  

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
        view.setLayoutParams(params);  
        final Scene newScene = new Scene(mContentParent, view);  
        transitionTo(newScene);  
    } else {  
        //把这个view加入到mContentParent中了,同时layoutparam为MATCH_PARENT 
        mContentParent.addView(view, params); 
    }  
    mContentParent.requestApplyInsets();  
    final Callback cb = getCallback();  
    if (cb != null && !isDestroyed()) {  
        cb.onContentChanged();  
    }  
} 

源码中看出mContentParent是在installDecor方法中初始化的:

private void installDecor() {  
       if (mDecor == null) {  
           //初始化mDecor这个View 
           mDecor = generateDecor();
           mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  
           mDecor.setIsRootNamespace(true);  
           if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {  
               mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);  
           }  
       }  
       if (mContentParent == null) {  
           //通过mDecor对象初始化mContentParent 
           mContentParent = generateLayout(mDecor); 
           .....  
       }  
.....  
}  

可以看到通过generateDecor方法生成了DecorView,这个DecorView其实也是所有应用窗口的根View:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { 
   ...}

在generateLayout方法中初始化了mContentParent对象:

protected ViewGroup generateLayout(DecorView decor) {  
        // Apply data from current theme.应用当前theme的数据,下面一连串的if就是根据你设置的theme来判断当前加载的形式的
        TypedArray a = getWindowStyle();  
        .........  
        //设置style为Window_windowNoTitle或者Window_windowActionBar
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }
        ....
        //设置是否全屏
        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
        }
        ......//一大堆if判断style资源
        //这里开始根据gradle中的targetSdkVersion来判断是否需要加载菜单栏
        final Context context = getContext();
        final int targetSdk = context.getApplicationInfo().targetSdkVersion;
        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
        final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
                R.bool.target_honeycomb_needs_options_menu);
        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
        } else {
            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
        }
        //高端设备上的非浮动窗口必须放在系统栏下方,因此必须知道这些窗口的可见性变化。
        // Non-floating windows on high end devices must put up decor beneath the system bars and
        // therefore must know about visibility changes of those.
        if (!mIsFloating && ActivityManager.isHighEndGfx()) {
            if (!targetPreL && a.getBoolean(
                    R.styleable.Window_windowDrawsSystemBarBackgrounds,
                    false)) {
                setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                        FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
            }
        }
        ......//省略多个if判断
        WindowManager.LayoutParams params = getAttributes();
        ......//省略其他加载资源
        // Inflate the window decor
        //添加布局到DecorView,前面说到,DecorView是继承与FrameLayout,它本身也是一个ViewGroup,
        //而我们前面创建它的时候,只是调用了new DecorView,此时里面并无什么东西。而下面的步奏则是根据
        //用户设置的Feature来创建相应的布局主题。
        //举个例子,如果我在setContentView之前调用了requestWindowFeature(Window.FEATURE_NO_TITLE),
        //这里则会通过getLocalFeatures来获取你设置的feature,进而加载对应的布局,此时是加载没有标题栏的主题.
        //这也就是为什么我们在代码中设置Theme或者requesetFeature()的时候必须在setContentView之前的原因.
        int layoutResource;
        //记得上文中的requestFeature,然后就可以通过getLocalFeatures方法获取了。。其实在activity中可以调用requestFeature 
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = 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
            ......//一大堆else if条件判断
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;    ③
            // System.out.println("Simple!");
        }
        mDecor.startChanging();
        //选择对应布局创建添加到DecorView中
        View in = mLayoutInflater.inflate(layoutResource, null);
        //往DecorView中添加子View,即mContentParent
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  
        mContentRoot = (ViewGroup) in;
        .........
        //获取id=content的view作为contenparent 
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);   
         .........
        mDecor.finishChanging();
        return contentParent;
   }  

可以看见上面方法主要作用就是根据窗口的风格修饰类型为该窗口选择不同的窗口根布局文件。mDecor做为根视图将该窗口根布局添加进去,然后获取id为content的FrameLayout返回给mContentParent对象。所以installDecor方法实质就是产生mDecor和mContentParent对象。另一方面如果要设置窗口风格,必须放在setContentView的前面:

  • requestWindowFeature(Window.FEATURE_NO_TITLE);//getLocalFeatures方法中被获取
  • setContentView(R.layout.test_layout);

View tree结构图:

这里写图片描述

DecorView果然作为根View,其下有三个子view。id为statusBarBackgroud和id为navigationBarBackground的View,分别表示手机的顶部的状态栏和手机底部的导航栏。然后是一个LinearLayout,这个LinearLayout是根据窗口的风格修饰类型而为该窗口选择不同的窗口根布局文件:上图为窗口风格是NoTitle,同时没有actionbar的时候。

LinearLayout包括一个@+id/action_mode_bar_stub的ViewStub,一个@android:id/content的FrameLayout。在setContentView中,PhoneWindow的setContentView方法中的下面两种方式都把这个FrameLayout作为了main_activity.xml的根视图:
1. mLayoutInflater.inflate(layoutResID, mContentParent);
2. mContentParent.addView(view, params);

inflate内部其实还是调用addview,然后一直如果该view是viewgroup,那么viewgroup又会把其中所有的子view都add进去,所以最后view就形成了一个视图层次。
DecorView是顶级View,它可以标示整个屏幕,其中包含状态栏,导航栏,内容区等等。这里的mContentParent指的是屏幕显示的内容区,而我们设置的activity_main.xml布局实际上是在一个id为content的FrameLayout中的,这个FrameLayout也就是前面一直提到的mContentParent!(我们看源码的话就会发现,不止screen_simple.xml,screen_toobar.xml,screen_title.xml等等布局文件中都含有这个id为content的FrameLayout)

  • screen_simple.xml源码:R.layout.screen_simple就是当我们的theme设置为NoTitleBar时的布局.也就是DecorView下的LinearLayout
<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" android:theme="?attr/actionBarTheme" />
    <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 
  • screen_title.xml.xml源码:当窗口风格是NoTitle,同时没有actionbar时的布局,也就是DecorView下的LinearLayout
<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" android:theme="?attr/actionBarTheme" />  
    <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>  

所有的view的根viewgroup就是上面的decorview,decorview的添加

final void handleResumeActivity(IBinder token,  
        boolean clearHide, boolean isForward, boolean reallyResume) {  
    // If we are getting ready to gc after going to the background, well 
    // we are back active so skip it. 
    ......  
    // TODO Push resumeArgs into the activity for consideration 
    ActivityClientRecord r = performResumeActivity(token, clearHide);  

    if (r != null) {  
        ......  
        // If the window hasn't yet been added to the window manager, 
        // and this guy didn't finish itself or start another activity, 
        // then go ahead and add the window. 
        ......  
        // If the window has already been added, but during resume 
        // we started another activity, then don't yet make the 
        // window visible. 
        ......  
        // The window is now visible if it has been added, we are not 
        // simply finishing, and we are not starting another activity. 
        if (!r.activity.mFinished && willBeVisible  
                && r.activity.mDecor != null && !r.hideForNow) {  
            ......  
            if (r.activity.mVisibleFromClient) {  
                r.activity.makeVisible();  
            }  
        }  
        ......  
    } else {  
        // If an exception was thrown when trying to resume, then 
        // just end this activity. 
        ......  
    }  
}  

handler机制我们知道了启动Activity其实都会启动activityThread的main方法,这个方法里面会创建主线程的looper。启动Activity调用完ActivityThread的main方法之后,接着调用ActivityThread类performLaunchActivity来创建要启动的Activity组件,在创建Activity组件的过程中,还会为该Activity组件创建窗口对象和视图对象;接着Activity组件创建完成之后,通过调用ActivityThread类的handleResumeActivity将它激活。(在onCreate中调用了setContentView,所以把除了decorView之外的所有的view都已经添加进去了。)handleResumeActivity中把decorView添加进去了handleResumeActivity方法中调用了r.activity.makeVisible()。

void makeVisible() {  
    if (!mWindowAdded) {  
        ViewManager wm = getWindowManager(); 
        wm.addView(mDecor, getWindow().getAttributes()); 
        mWindowAdded = true; 
    }  
    mDecor.setVisibility(View.VISIBLE); 
}  

另一方面,wm.addView实际上调用的是WindowManagerGlobal中addView方法,此时创建了ViewRootImpl对象。。ViewRootImpl有木有很熟悉,在绘制View过程中,就是从ViewRootImpl的performTraversals方法开始的,然后依次经过测量,布局,绘制过程。。invalidate其实最后也是调用了ViewRootImpl的performTraversals方法。

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {  
      ............  
      ViewRootImpl root;  
      View panelParentView = null;  

............  
      root = new ViewRootImpl(view.getContext(), display);  
      view.setLayoutParams(wparams);  
      mViews.add(view);  
      mRoots.add(root);  
      mParams.add(wparams);  
  }   
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/153253.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • quartus波形仿真破解MODELSIM「建议收藏」

    quartus波形仿真破解MODELSIM「建议收藏」(1)对于Quartus14.0安装时自动安装了modelsim的ae和ase两个版本,分别位于D:\ProgramFiles\altera\14.0\modelsim_ase和D:\ProgramFiles\altera\14.0\modelsim_ae目录下(根据自己的安装路径而定)。其中ae是免费的,包含有altera的FPGA芯片,而ase除了altera的外,还有xilinx的,库更…

  • matlab在极坐标中绘图y=sin(6x)_极坐标中θ范围怎么求

    matlab在极坐标中绘图y=sin(6x)_极坐标中θ范围怎么求在极坐标中绘图TryThisExampleTryThisExampleTryThisExampleTryThisExampleTryThisExampleTryThi

  • 2021年美赛A题思路详解

    2021年美赛A题思路详解2021年数模美赛A题思路详解题目分析思路详解由于和队友思路不一致,导致最后我的思路只算了前两问,而后几问用了我认为离题的PCA(主成分分析)的方法,我的建模思路没有得到完全实现,总体情况很不满意,特此写下这篇文章。题目分析从题目前面所提供的背景知识可以看出,C指出分解速率与菌丝伸长速率成正相关关系,我队友认为是线性关系而我认为是对数近似的关系。第二长图给了一个正比的关系,但是坐标却很容易理解错。这个moisturetrde-off不是湿度耐受性(moisturenichewidth),更

  • aero是什么意思啊_自动驾驶视觉算法

    aero是什么意思啊_自动驾驶视觉算法数据集介绍aeroscapes数据集下载链接AeroScapes航空语义分割基准包括使用商用无人机在5到50米的高度范围内捕获的图像。该数据集提供3269张720p图像和11个类别的真实掩码。数据加载dataloder写法(基于pytorch)由于该数据集提供了掩码图,因此不需要进行掩码图转换。下载完成后,文件结构如下:ImageSets文件夹:存放了两个txt文件,划分了训练集和验证集。JPEGImages文件夹:存放了RGB图像。SegmentationClass

  • 模逆矩阵「建议收藏」

    模逆矩阵「建议收藏」整数a对同余n之乘法模逆元是指满足以下公式的整数b乘法模逆元又称为数论倒数,其实可以看作是普通倒数在模算术中的推广。同理,乘法模逆矩阵可以看作是普通逆矩阵在模算术中的推广。例如求如下矩阵K的模26的乘法逆此时,求逆矩阵的如下公式依然有效,不过,里面的符号含义要推广到模算术中:这里,ad-bc=3×5-2×3=9,的含义不再是普通的倒数,而是数论倒数所以…

  • mac录屏软件推荐_mac 录屏软件

    mac录屏软件推荐_mac 录屏软件工作和生活中,我们常常需要录制电脑屏幕,例如老师和学生上网课、游戏大神分享操作技巧。那么,在Mac上有哪一些好用的录屏软件呢?作为一个工具软件重度爱好者,我整理了以下五大最好用的录屏软件,大家可以自行参考种草:一、FilmageScreenRecorderforMacFilmageScreen是一款简单易操作,功能十分强大的一站式视频软件,集屏幕录制、摄像头录制、音频录制、视频剪辑、视频格式转换为一体,可以说是真正满足你对于视频操作的所有需求。目前是只有Mac端,所以用Mac的童鞋们千万不.

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号