从源代码角度分析ViewStub 疑问与原理

一、提出疑问

ViewStub比較简单。之前文章都提及到《Android 性能优化 三 布局优化ViewStub标签的使用》。可是在使用过程中有一个疑惑,究竟是ViewStub上设置的參数有效还是在其包含的layout中设置參数有效?假设不明确描写叙述的问题,能够看下下面布局伪代码。

res/layout/main.xml
<LinearLayout >
    <ViewStub
        android:id="@+id/viewstub"
        android:layout_width="100dip"
          android:layout_marginTop="100dip"
        android:layout_height="wrap_content"
        android:layout="@layout/sub_layout"
        />

</LinearLayout>

res/layout/sub_layout.xml
<TextView
     android:layout_width="50dip"
     android:layout_marginTop="50dip"
     android:layout_height="wrap_content"
     android:text="ViewStub中包括的TextVeiw"/>

上面的代码中width终于效果是100dip还是50dip?marginTop是100dip还是50dip?带着这个问题一起看下Android 5.0源代码看看ViewStub原理。

为了便于把ViewStub与其infalte()载入出来的android:layout视图做个区分,下文中针对前者统一命名“ViewStub视图”。后者命名“被 载入视图”,仅为了描写叙述统一并不一定是专业名称。

二、分析ViewStub源代码

让ViewStub有两种方式一种是调用ViewStub.inflate() 第二种是设置ViewStub.setVisibility(View.VISIBLE); 事实上第二种方式依旧是调用的infalte方法,能够看例如以下ViewStub源代码。

    @Override
    @android.view.RemotableViewMethod
    public void setVisibility(int visibility) {
        if (mInflatedViewRef != null) {
            View view = mInflatedViewRef.get();
            if (view != null) {
                view.setVisibility(visibility);
            } else {
                throw new IllegalStateException("setVisibility called on un-referenced view");
            }
        } else {
            super.setVisibility(visibility);
            if (visibility == VISIBLE || visibility == INVISIBLE) {
                inflate();
            }
        }
    }

ViewStub复写了setVisibility方法,并在当中调用infalte方法。以下来看此方法源代码

public final class ViewStub extends View {
     ......

    public View inflate() {
        final ViewParent viewParent = getParent(); // 1 为什么能够直接获取父视图?

          // ViewStub的父视图必须是ViewGroup的子类
        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) { // ViewStub必须设置android:layout属性
                final ViewGroup parent = (ViewGroup) viewParent;
                final LayoutInflater factory;
                if (mInflater != null) {
                    factory = mInflater;
                } else {
                    factory = LayoutInflater.from(mContext);
                }
                // 2 inflate被载入视图
                final View view = factory.inflate(mLayoutResource, parent,
                        false);

                if (mInflatedId != NO_ID) {
                    view.setId(mInflatedId);
                }

                // 从父视图中获取当前ViewStub在父视图中的位置
                final int index = parent.indexOfChild(this);
                // 当前ViewStub也是个View只不过用来占位。所以先把占位的ViewStub视图删除
                parent.removeViewInLayout(this);

                // 3 此处获取的是ViewStub上面设置的參数
                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
                if (layoutParams != null) {
                    parent.addView(view, index, layoutParams);
                } else {
                    parent.addView(view, index);
                }

                // 目的是在复写的setVisibility方法中使用
                // 由于ViewStub.setVisibility操作的是被载入视图并不是当前ViewStub视图
                mInflatedViewRef = new WeakReference<View>(view);

                // 调用监听
                if (mInflateListener != null) {
                    mInflateListener.onInflate(this, view);
                }

                // 返回被载入视图,假设不须要当前能够忽略此返回对象
                return view;
            } else {
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
            }
        } else {
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
        }
    }
     ......
}

以下说下源代码中列出的几点。

 1. 为什么能够直接获取父视图?

ViewStub 继承自View其自身就是一个视图,其调用getParent()能够从父类View上入手、

public class View {
    public final ViewParent getParent() {
        return mParent;
    }

    void assignParent(ViewParent parent) {
        if (mParent == null) {
            mParent = parent;
        } else if (parent == null) {
            mParent = null;
        } else {
            throw new RuntimeException("view " + this + " being added, but"
                    + " it already has a parent");
        }
    }
}

从View的源代码中获取到,改动mParent參数的仅有assignParent方法且View中并未调用此方法,以下查看下其子类ViewGroup是否有调用。

public class ViewGroup {
    public void addView(View child, int index, LayoutParams params) {

        ......

        addViewInner(child, index, params, false);
    }

    private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

        ......           

        // tell our children
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
            child.mParent = this;
        }

          ......
     }
}

从上面源代码能够看到在addView方法中会调用addViewInner,当中调用child.assignParent(this);,把自己全部子视图mParent都设置成当前ViewGroup。

从这一点也能够看出,ViewStub本身是一个View且载入的时候就已经加入到视图树中(View Tree)中,仅接着有另外一个问题既然页面显示的时候ViewStub已经被加入到界面上。为什么有看不到ViewStub视图呢?

疑问:为什么ViewStub尽管是懒载入。可是其自身是一个视图且展示界面就会加入到视图树中,为什么看不到ViewStub?

public final class ViewStub extends View {

    public ViewStub(Context context) {
        initialize(context);
    }

     private void initialize(Context context) {
        mContext = context;
        setVisibility(GONE); // 初始化时把自己设置为隐藏
        setWillNotDraw(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(0, 0); // 全部子视图都设置为宽高为0
    }

    @Override
    public void draw(Canvas canvas) { // 不正确自身与子视图进行绘制
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
    }
}

从以上源代码能够看出ViewStub用尽全部办法让自己加入到视图树上是不显示ViewStub自身。

2. inflate被载入视图

再来看下载入android:layout视图的源代码。

final View view = factory.inflate(mLayoutResource, parent, false);

能够看到通过infalte方法记载的。其三个參数(int resource, ViewGroup root, boolean attachToRoot),各自是:

mLayoutResource : 设置的android:layout的值

parent : 通过getParent()获取即ViewStub的父视图

false : attachToRoot设置为false说明忽略androd:layout中根节点的layoutParams參数,即width=50dip与margin50dip

3. 视图加入ViewStub.getLayoutParams參数

此处源代码的是获取ViewStub.getLayoutParams參数设置到anroid:layout载入的视图上。 即width=100dip与marginTop=100dip生效。

三、总结

开头的疑问的答案。inflate出来的视图width=100dip与marginTop=100dip而android:layout视图中设置的width50dip和marginTop=50dip失效,等于没有设置。

ViewStub的原理简单描写叙述是

1. ViewStub本身是一个视图。会被加入到界面上,之所以看不到是由于其源代码设置为隐藏与不绘制。

2. 当调用infalte或者ViewStub.setVisibility(View.VISIBLE);时(两个都使用infalte方法逻辑),先从父视图上把当前ViewStub删除。再把载入的android:layotu视图加入上

3. 把ViewStub layoutParams 加入到载入的android:layotu视图上。而其根节点layoutParams 设置无效。

4. ViewStub是指用来占位的视图,通过删除自己并加入android:layout视图达到懒载入效果

时间: 2025-01-13 09:06:39

从源代码角度分析ViewStub 疑问与原理的相关文章

从源码角度分析ViewStub 疑问与原理

一.提出疑问 ViewStub比较简单,之前文章都提及到<Android 性能优化 三 布局优化ViewStub标签的使用>,但是在使用过程中有一个疑惑,到底是ViewStub上设置的参数有效还是在其包括的layout中设置参数有效?如果不明白描述的问题,可以看下以下布局伪代码. res/layout/main.xml <LinearLayout > <ViewStub android:id="@+id/viewstub" android:layout_w

Android的Message Pool是个什么鬼,Message Pool会否引起OOM——源代码角度分析

引言 Android中,我们在线程之间通信传递通常採用Android的消息机制,而这机制传递的正是Message. 通常.我们使用Message.obtain()和Handler.obtainMessage()从Message Pool中获取Message.避免直接构造Message. 那么Android会否由于Message Pool缓存的Message对象而造成OOM呢? 对于这个问题,我能够明白的说APP***不会因Message Pool而OOM***.至于为什么,能够一步步往下看,心急

从虚拟机指令执行的角度分析JAVA中多态的实现原理

从虚拟机指令执行的角度分析JAVA中多态的实现原理 前几天突然被一个"家伙"问了几个问题,其中一个是:JAVA中的多态的实现原理是什么? 我一想,这肯定不是从语法的角度来阐释多态吧,隐隐约约地记得是与Class文件格式中的方法表有关,但是不知道虚拟机在执行的时候,是如何选择正确的方法来执行的了.so,趁着周末,把压箱底的<深入理解Java虚拟机>拿出来,重新看了下第6.7.8章中的内容,梳理一下:从我们用开发工具(Intellij 或者Eclipse)写的 .java 源程

JDBC数据源DBCP源代码情景分析

在之前的一篇博文从JDBC到commons-dbutils 中,我曾经写到,对于获取数据库连接,有一个解决方案,那就是数据源.业界用到的比较普遍的开源数据源解决方案有很多,DBCP是其中一种,今天,我试图从源代码角度去解读这个解决方案. 全文分为三节,第一节介绍DBCP一般的用法,第二节按照第一节中给出的用法,从源代码角度看看,到底程序经过了哪些步骤,第三小节对全文做一个总结. 1.DBCP的一般用法 DBCP的用法其实很简单,一般就是new一个BasicDataSource,然后设置参数,当需

FFmpeg源代码简单分析:avformat_open_input()

本文简单分析FFmpeg中一个常用的函数:avformat_open_input().该函数用于打开多媒体数据并且获得一些相关的信息.它的声明位于libavformat\avformat.h,如下所示. /** * Open an input stream and read the header. The codecs are not opened. * The stream must be closed with avformat_close_input(). * * @param ps Po

从程序员的角度分析微信小程序

昨天朋友圈被微信小程序刷爆了. 我赶快在书架上拿出三年前买的书,把上面的土擦干净,压压惊. 作为一个并不是资深的程序员. 从程序员的角度分析一下微信小程序,欢迎指点. 首先吐槽 微信小程序只发了200个邀请号,和我预想的一样,张小龙并没有翻我牌,难道就不能雨露均沾吗? 先来了解下什么是微信小程序. 转自知乎 微信也许重申了"我们是一款约炮软件" 微信还提供了一大堆接口和组件(不好意思,说了句废话). 下面是禅叔的观点: 小程序原理就是用JS调用底层native组件,和React Nat

x264源代码简单分析:宏块编码(Encode)部分

本文记录x264的 x264_slice_write()函数中调用的x264_macroblock_encode()的源代码.x264_macroblock_encode()对应着x264中的宏块编码模块.宏块编码模块主要完成了DCT变换和量化两个步骤. 函数调用关系图 宏块编码(Encode)部分的源代码在整个x264中的位置如下图所示. 单击查看更清晰的图片 宏块编码(Encode)部分的函数调用关系如下图所示. 单击查看更清晰的图片 从源代码可以看出,宏块编码模块的x264_macrobl

转:x264源代码简单分析:编码器主干部分-1

本文来自:http://blog.csdn.net/leixiaohua1020/article/details/45644367 本文分析x264编码器主干部分的源代码."主干部分"指的就是 libx264中最核心的接口函数--x264_encoder_encode(),以及相关的几个接口函数 x264_encoder_open(),x264_encoder_headers(),和x264_encoder_close().这一部分源代码 比较复杂,现在看了半天依然感觉很多地方不太清晰

x264源代码简单分析:编码器主干部分-1

本文分析x264编码器主干部分的源代码."主干部分"指的就是libx264中最核心的接口函数--x264_encoder_encode(),以及相关的几个接口函数x264_encoder_open(),x264_encoder_headers(),和x264_encoder_close().这一部分源代码比较复杂,现在看了半天依然感觉很多地方不太清晰,暂且把已经理解的地方整理出来,以后再慢慢补充还不太清晰的地方.由于主干部分内容比较多,因此打算分成两篇文章来记录:第一篇文章记录x264