文档章节

分析Activity的View绑定过程

A
 Acidsweet
发布于 2014/05/05 16:47
字数 1015
阅读 82
收藏 1
在我们coding时,在Activity.onCreate()里面常用的教科书式的代码是:
setContentView(R.layout.main);
然后我们就可通过:
View view=findViewById(R.id.helloworld);
获取某个控件,但是这一切是如何完成的,本文会去探讨一下。
进入Activity的源码,查看setContentView()和findViewById()这两个函数查看原型:
/***
     * 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.
     */
     public void setContentView( int layoutResID) {
        getWindow().setContentView(layoutResID);
    }
     /***
     * Set the activity content to an explicit view.  This view is placed
     * directly into the activity's view hierarchy.  It can itself be a complex
     * view hierarhcy.
     *
     * @param view The desired content to display.
     */
     public void setContentView(View view) {
        getWindow().setContentView(view);
    }
他们都是通过getWindow()获得一个Windows的实例化对象的相关方法。
Window是个抽象类。这个Window的子类是什么呢?

PhoneWindow,至于为什么是它,这里暂不讨论。有兴趣的同学可以查看这篇博客:

http://www.cppblog.com/fwxjj/archive/2013/01/13/197231.html

下面我们进入PhoneWindow的源码看一看。PhoneWindow在com.android.internal.policy.impl包下面。
@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
上面是setContentView在PhoneWindow里的实现,而findViewById()在Window里面实现:
/**
     * Finds a view that was identified by the id attribute from the XML that
     * was processed in {@link android.app.Activity#onCreate}.  This will
     * implicitly call {@link #getDecorView} for you, with all of the
     * associated side-effects.
     *
     * @return The view if found or null otherwise.
     */
    public View findViewById(int id) {
        return getDecorView().findViewById(id);
    }
下面我们挨个分析,首先是setContent(),其中关键的代码是这行:
mLayoutInflater.inflate(layoutResID, mContentParent);
它将layoutResID所指向的布局实例化成view然后挂在了mContentParent后面。挂接上去之后剩下的就是view的绘制工作了,这个按下不表。
接着是findViewById(),它是调用DecorView.findViewById()。 DecorView是PhoneWindow的内部类,它表示整个window的顶层view,默认情况下它有两个子view,一个就是显示灰色的标题(也就是titlebar),另一个就是程序的显示区域。关于DecorView更详细的资料,大家可以参考: http://www.cnblogs.com/beenupper/archive/2012/07/13/2589749.html
DecorView其实是FrameLayout的子类,跟进FrameLayout!!!FrameLayout里面没发现findViewById().....进入FrameLayout的父类ViewGroup还是没有发现,继续深入...直到View。Bingo!
/**
     * Look for a child view with the given id.  If this view has the given
     * id, return this view.
     *
     * @param id The id to search for.
     * @return The view that has the given id in the hierarchy or null
     */
    public final View findViewById(int id) {
        if (id < 0) {
            return null;
        }
        return findViewTraversal(id);
    }
它非常爽快的调用了findViewTraversal(),但是注意ViewGroup是Override了这个方法的,所以需要进入ViewGroup查看findViewTraversal()。
/**
     * {@hide}
     */
    @Override
    protected View findViewTraversal(int id) {
        if (id == mID) {
            return this;
        }
 
        final View[] where = mChildren;
        final int len = mChildrenCount;
 
        for (int i = 0; i < len; i++) {
            View v = where[i];
 
            if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
                v = v.findViewById(id);
 
                if (v != null) {
                    return v;
                }
            }
        }
 
        return null;
    }
这里还是有地方需要注意的:
首先是:
if (id == mID) {
             return this ;
        }
mID是本View的ID,也就是说findViewById()是可以find到baseview的。然后就是:
final View[] where = mChildren;
         final int len = mChildrenCount;
         for ( int i = 0 ; i < len; i ++ ) {
            View v = where[i];
 
             if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0 ) {
                v = v.findViewById(id);
 
                 if (v != null) {
                     return v;
                }
            }
        }
         return null;
获取ViewGroup的所有子view,然后循环的查询子view中是否有view.getId()==id。这里其实还隐藏了一个递归过程,在< Android的UI两大基石>中,我们知道View和ViewGroup是使用Composite的组合模式,ViewGroup是View的子类。所以v.findViewById()可能调用View或者ViewGroup的findViewById(),这里也贴上View.findViewById()的源码。
/**
     * Look for a child view with the given id.  If this view has the given
     * id, return this view.
     *
     * @param id The id to search for.
     * @return The view that has the given id in the hierarchy or null
     */
    public final View findViewById(int id) {
        if (id < 0) {
            return null;
        }
        return findViewTraversal(id);
    }
View.findViewById()只是检查id是不是等于View自己的id,这样就保证了这个深度优先遍历的收敛性。
通过findViewById()我们知道了在Activity里面使用findViewById()其实是调用DecorView这个Window的顶层view的findViewById();
我们更知道了为什么有的时候findViewById()会返回null,为什么我们有的时候需要指定findViewById()的baseView,形如:
baseView.findViewById(R.id.helloworld);

--------------------------------------------------------------------------------

如果文中有任何错误,欢迎指出。

© 著作权归作者所有

A
粉丝 0
博文 15
码字总数 13381
作品 0
黄浦
私信 提问
加载中

评论(0)

Android M Launcher3主流程源码浅析

背景 关于Launcher是啥的问题我想这里就没必要再强调了。由于一些原因迫使最近开始需要研究一下Launcher3源码,为了不再像以前那么傻逼(研究Settings等代码没作笔记),故这里赶紧将阶段性的...

aweiloveandroid
2017/06/29
0
0
Flutter 49: 图解 Flutter 与 Android 原生交互

小菜上一篇简单学习了一下 Android 原生接入 Flutter Module,现在学习一下两者之间的数据交互; Flutter 与 Android/iOS 之间信息交互通过 Platform Channel 进行桥接;Flutter 定义了三种不...

阿策神奇
2019/06/19
0
0
Android窗口机制(四)ViewRootImpl与View和WindowManager

Android窗口机制系列 Android窗口机制(一)初识Android的窗口结构 Android窗口机制(二)Window,PhoneWindow,DecorView,setContentView源码理解 Android窗口机制(三)Window和WindowMan...

亭子happy
2019/02/26
21
0
关于Android MvvM的一些体会

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zly921112/article/details/82849549 前言 由于我司项目较老有很多历史包袱代码结构也比较混乱,需求复杂的页...

zhuliyuan丶
2018/09/26
0
0
灵动分析核心技术详解

TalkingData 灵动分析是一个产品和运维使用的工具,他是用来分析App使用者是如何使用一个App的,通过对发布后的App进行动态埋点,解决了两大疑问: 第一,埋点不再需要在发布前预先编码; 第...

TalkingData
2015/10/26
327
0

没有更多内容

加载失败,请刷新页面

加载更多

00-Java 面试准备

面试之前 面试前准备简历需要注意的几个方面: 写简历、改简历,这个一定要干的。简历有两个作用,一个是吸引别人,能让别人邀请你去面试,这是前提;另一个是引导面试的人,让面试的人问你所...

源程序
今天
54
0
OSChina 周二乱弹 —— 大王(@罗马的王)颜值制霸Osc社区

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @巴拉迪维 :Lunik的单曲《Seeing You Soar》 I hope you’re smiling,When seeing me soar. #今日歌曲推荐# 《Seeing You Soar》- Lunik 手...

小小编辑
今天
83
0
wordcount代码

1.写出map类 public class WCMapper extends Mapper<LongWritable,Text,Text,LongWritable>{ @Override protected void map(LongWritable key,Text value,Context context)throws IOExcepti......

七宝1
今天
59
0
Spring Batch 小任务(Tasklet)步骤

Chunk-Oriented Processing不是处理 step 的唯一方法。 考虑下面的一个场景,如果你仅仅需要调用一个存储过程,你可以在 ItemReader 中实现这个调用,然后在存储过程完成调用后返回 null。这...

honeymoose
今天
67
0
Linux日志分析

1. Linux日志文件的类型 2. 系统服务日志 2.1 syslogd的简介 2.2 syslogd的配置和使用 2.3 日志的安全性设置 2.4 远程日志记录服务 3. 日志的轮替 3.1 logrotate简介 3.2 logrotate的配置 3....

JiaMing
昨天
67
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部