Android 从SetContentView()谈起

本文深入解析了Android中Activity布局的加载过程,从源码角度详细分析了setContentView方法的实现原理,以及DecorView的创建与初始化流程。

当我们新建一个工程的时候,选择生成一个Activity,AS会自动给我们生成一个界面,那么这个界面是怎么生成的呢,下面我们就来分析一下:

Activity中的代码:

@Overrideprotected
 void onCreate(Bundle savedInstanceState) {    
          super.onCreate(savedInstanceState);    
          setContentView(R.layout.activity_main);}
复制代码

XML中的代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"   
 android:layout_width="match_parent"    
 android:layout_height="match_parent">  
  <EditText     
     android:layout_width="match_parent"     
     android:layout_height="match_parent"   
     android:layout_margin="15dp"     
     android:gravity="top"     
     android:hint="这是一个EditTextView"       
     android:inputType="textMultiLine"  
     android:maxLines="20"     
     android:minLines="6"     
     android:textSize="30sp" />
  </RelativeLayout>
复制代码

用Hierarchy viewer工具来查看一下结构图: ![@P8]6~ZC7~MJ~LJFG(DPBWA.png](http://upload-images.jianshu.io/upload_images/587163-e85daee233003243.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

DecorView以及它的子View一目了然,先看下SetContentView()的实现:

@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) {      
   installDecor(); 
   } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {   
   mContentParent.removeAllViews();   
   }   
 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {   
     final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                getContext());      
     transitionTo(newScene);   
 } else {    
    mLayoutInflater.inflate(layoutResID, mContentParent);   
 }  
  mContentParent.requestApplyInsets();  
  final Callback cb = getCallback();  
  if (cb != null && !isDestroyed()) {    
    cb.onContentChanged();   
 }}
复制代码
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
//mContentParent 是放置窗体内容的容器,也就是我们 setContentView()
 //时所加入的 View 视图树
private ViewGroup mContentParent;
复制代码

DecorView是PhoneWindow的一个内部类,同时DecorView也是Activiy的顶级View,一般来说DecorView的内部包括导航栏(NavigationBar)和状态栏(StatusBar),但这个会随着主题的变化而发生改变。刚开始mContentParent的值是null,所以会走installDecor():

if (mDecor == null) {   
   mDecor = generateDecor(); 
   mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 
   mDecor.setIsRootNamespace(true);  
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {  
      mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);    
  }}
if (mContentParent == null) { 
   mContentParent = generateLayout(mDecor);   
 // Set up decor part of UI to ignore fitsSystemWindows if appropriate.   
   mDecor.makeOptionalFitsSystemWindows();  
   final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);
复制代码
protected DecorView generateDecor() {  
  return new DecorView(getContext(), -1);
}
复制代码

在installDecor()里面通过调用generateDecor()方法来初始化DecorView,此时DecorView什么都没有,只是一个空的FrameLayout,往下走,来到generateLayout(mDecor),跳过一些主题布局设置,直接来到关键代码:

mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {  
  throw new RuntimeException("Window couldn't find content container view");
}
复制代码
/** * The ID that the main layout in the XML layout file should have. */
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
复制代码

DecorView通过generateLayout(mDecor)加载到具体的布局文件,具体的布局文件和系统版本以及主题有关,ID_ANDROID_CONTENT对应的即是R.id.content,也是我们通过SetContentView()中设置的布局id,走完installDecor(),我们继续看PhoneWindow中的SetContentView()方法,

if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
  final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());  
  transitionTo(newScene);
 } else {   
 mLayoutInflater.inflate(layoutResID, mContentParent);
 }
复制代码

接下来就简单了,mLayoutInflater.inflate(layoutResID, mContentParent),因为前面通过installDecor()创建了DecorView,因此这一步就是将Activity的布局(layoutResID)添加到mContentParent中了,到这里为止,Activity的布局文件就已经添加到DecorView里面了,继续看SetcontentView():

mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {  
  cb.onContentChanged();
}
复制代码

由于Activity实现了Window的Callback接口,

并且Activity的布局文件已经添加到DecorView里的mContentParent里了,当执行cb.onContentChanged()后,Activity就会回调onContentChanged()方法,由于Activity的onContentChanged()是个空实现,我们可以在子Activity中处理这个回调处理相应逻辑,到这里为止DecorView就已经被创建并初始化完毕。

转载于:https://juejin.im/post/5a33e7fd6fb9a0452341f3b8

### 回答1: setContentViewAndroid Studio中的一个方法,用于设置Activity的布局文件。它的作用是将布局文件与Activity进行关联,使得Activity能够显示出布局文件中定义的UI界面。在使用setContentView方法时,需要传入一个布局文件的资源ID作为参数,例如:setContentView(R.layout.activity_main)。这样,Activity就会显示出activity_main.xml文件中定义的UI界面。 ### 回答2: Android Studio是一款广泛使用的移动应用开发平台,它简化了Android应用的开发过程。当我们使用Android Studio来开发Android应用时,我们需要使用setContentView()函数来指定应用程序的布局文件。setContentView()函数将一个布局文件与当前的Activity(活动)关联起来,以便在用户与应用程序交互时在屏幕上呈现出布局。下面是有关setContentView()函数的一些重要并易于理解的细节: 1. setContentView()函数的作用 setContentView()函数是Android Studio用来显示UI控件的基本方法之一。它指定了应用程序界面所使用的布局文件。换句话说,当我们想要在用户界面中包含按钮、文本、图像等UI控件时,必须使用setContentView()函数从布局文件中导入UI控件。 2. setContentView()函数的语法 setContentView()函数的语法很简单。在您的Activity(活动)中,您需要在onCreate()方法中调用此方法。以下是setContentView()函数的基本语法: setContentView(R.layout.your_layout_file); 在此示例中,R.layout.your_layout_file指的是包含应用程序UI控件的布局文件。 3. setContentView()函数的默认行为 如果您没有调用任何参数的setContentView(),则默认情况下将使用Activity(活动)的基本布局文件。基本布局在Android Studio的Activity XML文件模板中定义。 4. setContentView()函数的细节说明 在某些情况下,您可能需要更改Activity(活动)的布局文件。这可能是因为你需要更改UI控件的位置或你需要在用户执行特定的操作时添加更多的UI控件。如果这是你要做的事情,你必须调用setContentView()函数。每个Activity(活动)都应该具有自己的布局文件,这样的话导出的APK会比较快并且不会影响用户使用体验。 在总体上看,Android Studio的setContentView()函数是一个基本的方法,它通过将布局文件与Activity(活动)文件关联起来,为开发人员提供了在应用程序中显示各种UI控件的简单方法。无论你是一名开发人员还是一个初学者,如果你想在Android应用程序中使用用户界面,你必须要熟悉setContentView()函数的使用方法。 ### 回答3: setContentView()Android Studio中很常用的一个方法,它用于将XML文件中的布局加载到Activity中。在Android Studio中,开发者可以使用可视化设计工具创建和编辑XML布局文件,然后在Activity中调用setContentView()方法来加载该布局。 在调用setContentView()时,需要传递一个XML布局文件的ID作为参数,示例如下: ```java setContentView(R.layout.activity_main); ``` 上述示例代码中,R.layout.activity_main指的是当前Activity使用的布局文件,可以根据实际情况进行修改。 通过setContentView()加载布局后,开发者就可以在Activity中对布局中的各个控件进行操作。比如,可以通过findViewById()方法获取控件的引用,然后对其进行修改或添加事件监听器等。 除了在Activity中调用setContentView()方法外,还可以在Fragment或Dialog等组件中使用该方法。不过,需要注意的是,不同的组件可能需要不同的布局文件,因此在调用setContentView()时要传递正确的布局文件ID。 总之,setContentView()Android Studio中十分重要的一个方法,开发者需要熟练掌握并灵活运用。通过合理使用该方法,可以使Android应用具有良好的用户界面体验。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值