setContentView源码学习

setContentView源码学习参考博客:https://blog.csdn.net/qq_17250009/article/details/52242895https://www.jianshu.com/p/40a9c93b5a8dhttps://www.jianshu.com/p/e42b638944ae特别说明,我用的API版本是25,这句话后面会用到,特别注意!!!今天突然有个想法:不同的布局文件,相…

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

参考博客:

https://blog.csdn.net/qq_17250009/article/details/52242895

https://www.jianshu.com/p/40a9c93b5a8d

https://www.jianshu.com/p/e42b638944ae

特别说明,我用的API版本是25,这句话后面会用到,特别注意!!!

今天突然有个想法:不同的布局文件,相同类型的控件有相同的ID,在findViewById的时候,为什么没有出现“系统不知道去引用哪个id的View的问题”?

然后写了个demo:2个Activity:A和B,它们对应加载的布局a、b中,有相同的控件TextView,这2个TextView,又是一样的id。Activity中分别引用,然后使用,没有任何问题。

抱着这个疑问,网上找了资料,又实际读了源码,这里,做个学习笔记。

首先,需要知道Activity展示布局控件的时候,屏幕上的窗口结构,如图:
这里写图片描述

说明:
1、每个Activity,都有一个自己的PhoneWindow(或者说window)
2、DecorView:顶层视图,将要显示的具体内容呈现在PhoneWindow上. DecorView是当前Activity所有View的祖先,它并不会向用户呈现任何东西
3、ContentView:是一个id为content的FrameLayout。我们平常在Activity使用的setContentView就是设置在这里,也就是在FrameLayout上

源码解读:
Activity中

public class MainActivity extends Activity { 
   

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }
}

进入setContentView方法,Activity中:

/** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. * * @see #setContentView(android.view.View) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

这里,我们看到了,一个实例调用了setContentView(layoutResID),我们去看看这个getWindow()给了我们什么:Activity中

/**
* 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的attach中:

 mWindow = new PhoneWindow(this, window);

关于Window和PhoneWindow,这里提一句:

public abstract class Window { 
   ......}

public class PhoneWindow extends Window implements MenuBuilder.Callback { 
   ......}

到这里,就证明了上面说明中的第一句话:每个Activity中,都会给自己创建一个PhoneWindow,这个PhoneWindow是抽象类Window的具体实现。

到了这里,我们就知道了,要看PhoneWindow中的setContentView

 @Override
 public void setContentView(int layoutResID) {
    ......
    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);
    }
    ......
    }

这里,我们看到了最熟悉的inflate方法,其中第一个参数,就是我们在Activity中设置的layout的资源ID,第二个是要展示这个layout的父控件(容器)。

也就是说,这里,会把我们指定的布局,填充到一个名叫mContentParent的父容器中。
而和mContentParent有关的,就是上面几行的代码,我们去installDecor()中看看

 private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        ......
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ......
    }
}

从源码上看,要得到mContentParent ,就需要先有mDecor,要有mDecor,就得调方法generateDecor(-1)

protected DecorView generateDecor(int featureId) {
    ......
    return new DecorView(context, featureId, this, getAttributes());
}

现在,有了DecorView mDecor,然后我们继续看generateLayout方法

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

其中:

Window中:
/** * 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;

这里,就证明了上面“说明”中的第3句话

结论:从上面的源码中,我们知道了流程:
1、我们在Activity中的setContentView,实际调用的是PhoneWindow的setContentView(PhoneWindow是抽象类Window的实现)
2、PhoneWindow的setContentView中,会去创建DecorView,作为展示窗口
3、创建一个id为content的布局mContentParent,作为容器
4、通过inflate(layoutResID, mContentParent);将我们传入的布局,填充到这个容器中
5、最后,我们就会看到我们写的布局

细心的人发现了,我上面没有提到给 DecorView 填充布局的代码,这是因为,在我的API 25下,PhoneWindow的 generateLayout 方法中,没找的对应的代码。但是在我参考的那几篇博客中,generateLayout 方法下,明确提到了

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);

考虑到之前大神写博客,距离我现在写这个博客,有段时间了,可能是API升级了。

因此,我认为,如果我没有找错或者忽略了某几行代码,就是API升级更了,有了新的实现方法我不知道。有知道的大神,请指教!

现在,回到最开始的问题上:为什么2个Activity,加载自己的布局,里面有同类型、同ID的控件不会报错?因为,每个Activity,有自己独立的展示窗口,加载的是自己设置的layout的布局ID,系统知道去哪里找,展示在哪里,没有数据重叠、歧义部分,自然,就不会报错了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/153240.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • C++和Java中STL库入门[通俗易懂]

    C++和Java中STL库入门[通俗易懂]C++和Java中STL库入门STL简介为什么使用STLSTL基本概念STL使用前的初始化C++里STL基本容器详解STL简介STL简称标准模版库,被容纳在C++标准程序库,包含了许多基本数据结构和基本算法,使程序员写起来得心应手。为什么使用STL在学习数据结构的时候,在程序中会使用到堆、栈、队列、链表等一些基本的算法,而学习数据结构的时候,这些基本算法写起来十分繁琐,如果不想写这些,那么就可以考虑一下STL了。但是不要太过于依赖STL!STL基本概念要使用STL,需要理解以下几个基本概念:

    2022年8月31日
    4
  • 分区容错性和可用性的区别

    分区容错性和可用性的区别分区容错性:因为网络等硬件引起的问题,一台服务器崩溃了,保证能在其他服务器上也能顺利完成业务。可用性:因为软件代码层面的问题,一台服务器上的服务崩溃了,保证能在其他服务器上完成该业务。区别:分区容错性更偏向于硬件引起的问题可用性更偏向于软件代码层面的问题…

    2022年7月25日
    25
  • 【博弈论】常见总结「建议收藏」

    【博弈论】常见总结「建议收藏」每次看到博弈论的题目就很脑阔疼平衡状态,又称作奇异局势。当面对这个局势时则会失败。任意非平衡态经过一次操作可以变为平衡态。先手能够在初始为非平衡的游戏中取胜,后手能够在初始为平衡的游戏中取胜。SG函数待补两个博客12来源12一、巴仕博弈(BashGame)1堆题目HDU1846有一堆物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光…

    2022年10月15日
    2
  • QUOTEName用法[通俗易懂]

    QUOTEName用法[通俗易懂]  1.可以用在拼接字符串时候将参数名称替换为参数值到SQL里面,如下面代码:DECLARE@TableNameVARCHAR(50),@SqlNVARCHAR(MAX),@DJIDINT;SET@TableName=’DJ’;SET@DJID=1991906354;SET@sql=’SELECT*FROM’+QUOTENAME(…

    2022年7月25日
    7
  • python十进制转二进制函数_python 十六进制转二进制

    python十进制转二进制函数_python 十六进制转二进制Python2python十进制转2进制有内置函数bin方法1:in:bin(1)output:’0b1’方法2:n=int(input(‘请输入要转换进制的数值:’))#x=2#转换为二进制,所以这里取x=2b=[]#存储余数whileTrue:#一直循环,商为0时利用break退出循环…

    2022年10月9日
    5
  • Perl正则表达式详解[通俗易懂]

    Perl正则表达式详解[通俗易懂]Perl中的正则表达式使用示例m//修饰符LVALUE=~m/PATTERN/;LVALUE=~/PATTERN/;修饰符含义/i匹配时忽略大小写/g全局匹配,默认情况下,正则表达式”abc”匹配”abcdabc”字符串的时候,将之匹配左边的abc,使用g将匹配两个”abc”/c在开启g的情况下,如果匹配失败,将不重置搜索位置/m多行匹配模式/s让.可以匹配换行符”\n”,也就是说该修饰符让.真的可以匹配任意字符/x允许正则表达

    2022年5月31日
    49

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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