Android-setContentView的二次理解

最近两天,看了很多关于android中view加载的过程。在网上看到很多大神的讲解,也深深的被透明透彻的讲解所折服,同时也非常感谢他们的无私奉献。 再次,我非常感谢如下的大神的博文:

郭霖:http://blog.csdn.net/guolin_blog/article/details/12921889

qinjuning:http://blog.csdn.net/qinjuning/article/details/7226787

本文也是根据他们的讲解,加上我个人的理解,更加细化的对view的加载过程给予说明。

首先,既然我们的研究对象的setContentView()方法,那么我们就查看该方法的具体实现:

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

再以上的语句是很简单的,但是内部做了很多的复杂的处理,这个处理,主要是交给了getWindow()返回的window类。但是如果你看到源码的化,你就会知道,实际上Window类是一个抽象类,代码如下:

  public abstract class Window {
    ...
    public abstract void setContentView(int layoutResID); 
    ...
  } 
    

一个抽象类的抽象方法,我们是无法直接调用的,这就说明,在android的框架内,必然是有一个window的实现类的,没错!这个实现类就是PhoneWindow。 我怎么就知道是PhoneWindow类而不是其他的呢?好吧!我们继续看代码,在Activity.java中有一个 Internal API 方法attach();我们只需要知道一句代码就好了。

 mWindow = PolicyManager.makeNewWindow(this);

我们顺着这条线,继续往下,查看PolicyManager.makeNewWindow()方法:

public PhoneWindow makeNewWindow(Context context) {
       return new PhoneWindow(context);
}

至此我们就可以得出,我们在Activity中使用setContentView()方法,实际上是交给一个叫做PhoneWindow的类来处理的。下面我们将着重去了解PhoneWindow的API。

注:如果没有下载android的源码的话,这个类你是看不到的。你如果需要查看的话,可以从此查看:
PhoneWindow.java

在此有必要提一下什么是PhoneWindow?

PhoneWindow:代表的就是你的当前屏幕所有的内容;
----|private DecorView mDecor:该类是一个PhoneWindow的内部类,实现于FrameLayout,代表了 PhoneWindow作用域当中的根布局。
----|private ViewGroup mContentParent:该成员变量就是我们的布局存放的位置。也就是说我们调用setContentView()当中传进去的布局就放置在该位置。其在布局中的id = content

对于以上的两个成员变量,如果不明白,暂且放置,一会我会做些许的说明。

在此,就只贴setContentView的代码即可,如需查看其他,点击上面的连接即可。

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

现在,我们开始分析PhoneWindow中的setContentView()方法。
首先会判断mContentParent是否为空,关于mContentParent的这个成员变量,我之前已经说过,它就是代表我们的布局要存放位置,所以是一个ViewGroup。当第一调用时,自然是为null的,此时会调用installDecor()方法。
下面分析: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) {
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                    View titleContainer =                 findViewById(com.android.internal.R.id.title_container);
                    if (titleContainer != null) {
                        titleContainer.setVisibility(View.GONE);
                    } else {
                        mTitleView.setVisibility(View.GONE);
                    }
                    if (mContentParent instanceof FrameLayout) {
                        ((FrameLayout)mContentParent).setForeground(null);
                    }
                } else {
                    mTitleView.setText(mTitle);
                }
            }
        }
    }

该方法的实际目的就是初始化mDecor和mContentParent。
现在分步分析初始化二者的代码;
首先会进行mDecor方法:会调用generateDecor()
该方法很简单,就是一个简单的创建对象的过程:

  protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
  }

这句没有什么好说的,关键是对mContentParent的初始化,该初始化过程会调用generateLayout() 该方法的内容很多,就不贴全部了,如果想要查看,可以点击我上述给的链接。
实际上对mContentParent的初始化主要是根据你的窗体的主题内容进行初始化,所以当你选择不同的theme 时,你的窗体的样式就会不一样,这里的不一样就是该步所生成的,因为该方法的第一行就是获取一个窗体的样式。

TypedArray a = getWindowStyle();  

而之后就是根据这些样式,进行窗体的初始化。

我就从填充我们的布局那部分开始吧:

mDecor.startChanging();

       View in = mLayoutInflater.inflate(layoutResource, null);
       decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

       ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
       if (contentParent == null) {
           throw new RuntimeException("Window couldn't find content container view");
       }

       if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
           ProgressBar progress = getCircularProgressBar(false);
           if (progress != null) {
               progress.setIndeterminate(true);
           }
       }

       // Remaining setup -- of background and title -- that only applies
       // to top-level windows.
       if (getContainer() == null) {
           Drawable drawable = mBackgroundDrawable;
           if (mBackgroundResource != 0) {
               drawable = getContext().getResources().getDrawable(mBackgroundResource);
           }
           mDecor.setWindowBackground(drawable);
           drawable = null;
           if (mFrameResource != 0) {
               drawable = getContext().getResources().getDrawable(mFrameResource);
           }
           mDecor.setWindowFrame(drawable);

           // System.out.println("Text=" + Integer.toHexString(mTextColor) +
           // " Sel=" + Integer.toHexString(mTextSelectedColor) +
           // " Title=" + Integer.toHexString(mTitleColor));

           if (mTitleColor == 0) {
               mTitleColor = mTextColor;
           }

           if (mTitle != null) {
               setTitle(mTitle);
           }
           setTitleColor(mTitleColor);
       }

       mDecor.finishChanging();
       return contentParent;

在以上代码中,请注意这一句:

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
明白了吗?这个函数的作用总结一下,就是根据在xml设置的样式,或者 在调用setContentView()之前,对窗体设置了一些参数的初始化过程,然后返回一个布局的持有对象,即我们要放置我们的布局的那个布局。有点绕。请细细体会。
下面我们再重新回到那个方法:

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

再次看到这个方法时,是不是就不那么陌生了呢?你会发现,在判断mContentParent时,会有else的分支,当它不会null的话,也就是说我之前已经调用过该方法,再次调用时,就不用在进行installDecor()方法了,因为我已经进行了初始化,现在只需要把我的布局加载到上面就可以了,要记住,我们的布局加载过程,并不是在installDecor()中进行的,而是在setContentView方法内部,而installDecor方法的目的就只是根据我设置的参数来初始化该Activity的窗体的样式,和返回一个放置我布局的一个布局的持有对象,只有获得了后者,我才可以将我的布局加载到activity中。
mLayoutInflater.inflate(layoutResID, mContentParent);这个方法,相信你一定明白,就是将我的布局,加载到父View mContentParent中。然后调用cb.onContentChanged();来更新activity的布局。 至此,完成以上步骤仅仅只是把你的布局绑定在当前的activity,要想显示到屏幕上还需要其他的步骤。

转载于:https://my.oschina.net/ccqy66/blog/524801

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答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应用具有良好的用户界面体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值