SetContentView都做了什么

Android:setContentView做了什么(PhoneWindow,DecorView) 这次我们来看看,你的xml布局文件,是如何被系统处理并显示的?布局文件被解析成View后,去了哪里? Android的四大组件之一Acitivty对于Android开发者再熟悉不过了,这个活动页面贯穿了整个App的运行,使得各个业务逻辑得以以可见视图的形式展现给用户,Activity上可以承载各种组件(包括Fragment),Activity加载用户视图,是由开发者在xml布局文件中定义好... 阅读详情

在Activity的onCreate方法中,可以调用setContentView(layout_id),来设置这个Activity的视图。那么setContentView都做了些什么呢?

以下一个简单的Activity的代码:

public class ViewDemoActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.view_demo);
    }
}

查看父类Activity的setContentView方法:

public void setContentView(int layoutResID) {
    getWindow().setContentView(layoutResID);
    ...
}
这个getWindow()指向哪里呢?我们可以在onCreate中打个log:
D/ViewDemoActivity(3969): com.android.internal.policy.impl.PhoneWindow
原来是PhoneWindow,来看看它的setContentView方法:

@Override
    public void setContentView(int layoutResID) {
        // 1
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }

        // 2
        mLayoutInflater.inflate(layoutResID, mContentParent);

        // 3
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
这大体可以分为三个步骤:

第一步:判断父容器是否为空

为空:生成Decor
不为空:删除 contentParent 所有的子控件。

第二步:解析layoutResID所代表的xml文件

LayoutInflater.inflate(...)方法的代码如下:
public View inflate(int resource, ViewGroup root) {
        return inflate(resource, root, root != null);
    }

    public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
        XmlResourceParser parser = getContext().getResources().getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context)mConstructorArgs[0];
            mConstructorArgs[0] = mContext;
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()   + ": No start tag found!");
                }
                final String name = parser.getName();

                // 根标签为merge时,root不能为空
                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    View temp;
                    if (TAG_1995.equals(name)) {
                        temp = new BlinkLayout(mContext, attrs);
                    } else {
                        temp = createViewFromTag(root, name, attrs);
                    }

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not attaching. 
                            // (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

                    // 解析temp的所有子View
                    rInflate(parser, temp, attrs, true);

                    // 将temp添加到root中
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // 如果未指定root或者不附加到root,则返回xml所代表的view;
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
            } catch (XmlPullParserException e) {
                InflateException ex = new InflateException(e.getMessage());
                ex.initCause(e);
                throw ex;
            } catch (IOException e) {
                InflateException ex = new InflateException(parser.getPositionDescription()+ ": " + e.getMessage());
                ex.initCause(e);
                throw ex;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;
            }
            return result;
        }
    }

inflate(...)最终会调用rInflate(...):
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        final int depth = parser.getDepth();
        int type;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final String name = parser.getName();

            if (TAG_REQUEST_FOCUS.equals(name)) {
                parseRequestFocus(parser, parent);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else if (TAG_1995.equals(name)) {
                final View view = new BlinkLayout(mContext, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true);
                viewGroup.addView(view, params);                
            } else {
                final View view = createViewFromTag(parent, name, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }

        if (finishInflate) parent.onFinishInflate();
    }

rInflate(...)先特殊处理了几个xml标签,然后在else分支,会递归调用rInflate(...),将xml的子控件添加到parent中,生成完整的contentView。

看了这里,同学们就会明白,为什么不建议在布局文件中做过多地View嵌套了吧,层层递归!

第三步:通知Callback,ContentView发生改变

这个getCallback()是什么?打个Log看看:
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.view_demo);
        Log.d("ViewDemo", getWindow().getCallback().toString());
    }

输出结果为:
D/ViewDemoActivity( 7228): cn.erhu.android.view.ViewDemoActivity@6562f2c0
原来它就是DemoActivity!
在Activity.java的attach()方法中:
final void attach(...) {
        ...
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        ...
    }
原来PhoneWindow的callback就是Activity,并且Activity的onContentChanged()方法是空的,所以我们可以在自己的Activity中重写这个方法,来监听ContentView发生改变的事件。



ViewBinding与Kotlin委托结合使用,去除setContentView,原理伪代码 ViewBinding与Kotlin委托结合使用,去除setContentView。 ViewBindingPropertyDelegate和hi-dhl/Binding库的伪代码,用来了解其内部实现原理。 相当于是手写了一个简单的ViewBindingDelegate和Binding库 可看我的博客有详细介绍 : https://blog.csdn.net/EthanCo/article/details/126739511 立即下载

相关推荐

FPGA远程升级避坑实录:用STARTUPE2原语搞定Flash时钟,K7325T实测有效

本文深入解析了FPGA远程升级中的STARTUPE2原语应用,特别针对K7325T芯片的Flash时钟问题提供了实测有效的解决方案。通过详细的原语工作机制、Vivado工程实现步骤及调试技巧,帮助工程师规避常见陷阱,提升远程升级的可靠性和效率。

weixin_42558093的博客 141

setContentView详解

setContentView我们在Activity中经常见到,它的作用就是把我们的布局文件放在Activity中显示,下面我们根据源码分析setContentView是如何做到的。

Allence的博客 1万+

博途WinCC多语言项目配置指南

在博途(TIA Portal)中为WinCC项目配置多语言功能,需通过系统化步骤实现界面文本的动态切换。

dhaimo的博客 2472

手机页面的切换(setContentView的应用)

手机页面的切换(setContentView的应用) 新建一个继承Activity类的SetContentViewActivity,并设置布局文件为:setcontentview1.xml和setcontentview2.xml。 首先在setcontentview1.xml的布局文件中定义一个TextView和一个Button组件,并设置背景颜色为红色。而后在setcontentview2.

lyx_xiaofeng的博客 1448

Databricks Genie:语义编译器架构与企业级智能解析实践

语义层是现代数据平台实现自然语言查询的核心基础,其本质是将业务术语映射为可执行、可治理的数据契约。原理上依赖结构化元数据、值字典与数据血缘三支柱协同,通过分层AI代理(如Intent Parsing、Retriever、Verifier)实现意图识别→语义对齐→安全执行的闭环。技术价值在于降低SQL使用门槛的同时保障审计性、一致性和权限可控性,适用于销售运营、财务分析等一线业务场景。本文深入解析Databricks Genie作为典型语义编译器的落地逻辑,涵盖Unity Catalog知识注入、七层Agen

weixin_29053695的博客 337

华为eNSP与VMware虚拟机联动实战:从零搭建仿真网络环境

本文详细介绍了如何从零开始搭建华为eNSP与VMware虚拟机的联动仿真网络环境。通过环境准备、网络适配器配置、eNSP与VMware网络集成、安全策略与NAT配置等步骤,帮助读者掌握构建企业级网络实验场景的关键技术。这种组合特别适合网络工程师进阶训练和华为认证考试准备,实现低成本、高灵活性的网络仿真实验。

weixin_30415113的博客 504

关于setContentView方法

转载请以链接形式标明出处: 本文出自:103style的博客 base on Android-29 文中相关的源码有删减 可以带着以下问题来看本文: 为什么从代码设置属性和主题,得在 setContentView 之前? setContentView 添加的 View 加载完成的回调方法? setContentView 的执行流程? LayoutInflater 的 inflate 方法不...

103style 5656

基于ROS2系统开发机器人

以上是基于ROS 2开发机器人的基本流程和一个简单的发布者-订阅者示例。通过这个流程,你可以逐步扩展和优化你的机器人系统,实现更复杂的功能。

xuweilin的博客 1495

你真的完全了解setContentView()么

在createViewFromTag()中我们会通过 tryCreateView()尝试创建View,并且在这里就调用了 mFactory2.onCreateView(parent, name, context, attrs)方法,mFactory2在AppCompatDelegateImpl#installViewFactory()方法中初始化的,他在AppCompatActivity#onCreate()中调用的!在ensureSubDecor() 方法中,我们会初始化系统的布局。恍然大悟,醍醐灌顶!

chuyouyinghe的专栏 890

【CASS精品教程】032:CASS11台阶画法案例大全

一、普通无边台阶 二、普通有边台阶 三、普通圆弧无边台阶 四、U型台阶 五、曲线U型台阶 六、L型台阶

智慧空间实验室 7465

机器人中的数值优化(十五)——PHR增广拉格朗日乘子法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考,主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等,本系列文章篇数较多,不定期更新,上半部分介绍无约束优化,下半部分介绍带约束的优化,中间会穿插一些路径规划方面的应用实例

慕羽★的博客 2002

Android setContentView详解

在 Android 应用开发的世界里,几乎是每个开发者都会接触到的方法。它的作用至关重要——负责将视图(View)或布局(Layout)展示在屏幕上。尽管这看起来是一个简单直接的操作,但其背后实际上隐藏着Android系统中复杂而精妙的窗口管理和视图渲染机制。最近,在与同行的交流中,我发现我几乎将这一块忘得一干二净。因此,我决定从Activity的方法入手,重新梳理并深入探讨 Android 的窗口管理和视图展示原理,希望能够为大家带来新的理解和启发。

xiaokangss的博客 4274

23种设计模式-代理(Proxy)设计模式

软考中级-软件设计师 代理设计模式

片言可决讼,半纸能缚龙 1784

setContentView() 究竟都做了什么?

转载请注明出处:http://blog.csdn.net/crazy1235/article/details/51471280这个函数是在常见不过的了!setContentView(R.layout.activity_test_view);但是调用了这个函数设置Activity布局的时候,android系统究竟做了什么操作呢?往下看!setContentView()我们新建项目模式都是使用的 The

crazy_jack 2万+

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

setContentView到底做了什么

对Activity的setContentView的使用,大家比较熟悉了,当然,对setContentView的原理估计也比较熟悉,网上有不少的文章,不过,还是写一篇这方面的东西,记录下,毕竟很多东西,看别人的是一回事儿,自己写又是一回事儿,当作是对知识的温故,再学习吧! Activity.java 首先,在自己的activity中,调用setContentView(int resID)方法

Rocky Kou的博客 1万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

setContentView做了什么

Activity是Android系统的一个展示型组件,负责向用户展示一个界面并且可以接收输入事件并进行处理。在开发应用时,我们会将要展示的界面写在xml里,在java代码里去处理自己的业务逻辑,那就有个疑问xml跟我们的java代码是怎么关联起来的呢,这就必须要看看onCreate的setContentView做了些什么。

Tank_Mao的博客 811

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Activity中setContentView做了什么

1. setContentView public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } 复制代码getWindow()方法返回了一个mWindow,在attach(...

weixin_33910460的博客 172

setContentView()做了什么事情?

setContentView()做了什么事情

backer 1974

Activity中的setContentView方法到底做了什么?

Activity中的setContentView方法到底做了什么?我们从setContentView开始了解View的加载过程Actvity中的onCreate方法中的setContentView到底做了些什么?(PS:以下代码是基于Android 25)我们看下Activity的setContentView源码,发现最终有掉用Window的setContentView方法(其实是Window的子类

liangdong520的专栏 1739

Activity的setContentView到底做了什么

Activity 的 setContentView getWindow().setContentView(layoutResID); Activity 的getWindow return mWindow; Activity 的attach mWindow = new PhoneWindow(this, window, activityConfigCallback); 很明显Activity 的 setContentView最终调用的是PhoneWindow的setContentView PhoneWi

codehxy的专栏 362

夜夜使用的setContentView()里面到底做了什么?

一、写在前面 我们都知道继承Activity的onCreate()方法然后setContentView(R.layout.xxx)可以设置我们自己的布局文件,但是布局文件生成的View到底添加到哪里去了呢?首先在Activity这个类中我们可以看到有个mWindow变量,这个变量是在attach()方法里面创建的:mWindow = new PhoneWindow(...),然后在我们setCon...

冰河世纪第二代 632

从源码分析 setContentView 做了什么

从源码分析 Activity 的 onCreate 方法的 setContentView 都做了什么。 public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); // 如果需要的话,初始化 ActionBar initWindowDecorActionBar...

hexiaosa91的博客 184

Android知识-setContentView做的一些事

从MainActivity的setContentView进入根据源码追踪找到AppCompatDelegate的实现类AppCompatDelegateImplV9,从AppCompatDelegateImplV9#setContentView中可以看出其源码 @Override public void setContentView(int resId) { ensureSubDecor(...

呆呆 610

(源码阅读)我们经常用的setContentView(),到底做了什么。

既然是源码阅读,那我们就果断上源码吧,首先就是Activity下的setContentView().嘿嘿 /** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @pa

GuiM_007的博客 467

Activity的OnCreate方法中的setContentView方法具体做了哪些事?

这个方法,其实会最终调用PhoneWindow的setContentView方法,下面看看PhoneWindow的源码:(PhoneWindow.java) @Override public void setContentView(int layoutResID) { //当mContentParent为null时,则执行installDecor() if (mContentParent == null) { installDecor(); //

weixin_42904469的博客 336
上一篇: 单例模式
下一篇: JavaScript中判断对象类型的几种方法
hunter800421
博客等级 码龄22年 60粉丝 97原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值