Android 使用View Gone 与 ViewStub的区别

作者: 林子木 (wolinxuebin)

一、结论

为了部分同学迅速查找结果,所以把结论放在第一段。区别如下:

  • 设置为GONE的View不会占用布局空间,但是会进行类的初始化;如ImageView 将src设置为一个BitmapDrawable,那么该图片将会加载到内中
  • ViewStub只有在代码中进行inflate之后才会加载进来,不会占用内存

二、一个简单的内存实验

实验的布局代码如下:

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity">    <com.example.linxuebin.testviewstubandgone.MyImageView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:src="@drawable/screen_bg"/>android.support.constraint.ConstraintLayout>

注:其中screen_bg是一张656x1167大小的png图片, MyImageView是完全继承ImageView,仅仅在关键部分打印一些信息。
通过将ImageView的visiable属性设置为visiable、invisiable、gone 和 使用viewStub得到如下内存图:

图1.1 通过将ImageView的visiable属性设置为visiable、invisiable、gone 和 使用viewStub的内存图

通过上图可以得到如下的分析结果:

  • Visiable VS InVisiable: 仅仅在Graphiscs上有差距,因为没有进行绘制操作
  • InVisiable VS Gone: 他两竟然没有如何的区别(忽略0.1的差距)
  • Gone VS ViewStub: Gone竟然比ViewStub多出3MB

Why?
下一章我们通过另外一个实验去探索这个原因可好?

三、ImageView的一些奥秘

以下是ImageView截取初始化的一段代码:

public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,            int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        ... #省略不相干代码        final TypedArray a = context.obtainStyledAttributes(                attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);        final Drawable d = a.getDrawable(R.styleable.ImageView_src);        if (d != null) {            setImageDrawable(d);        }      .... #省略不相干代码    }

发现ImageView在初始化的时候,会加载属性src指向的drawable资源。
为了获取加载的Drawable信息,我们通过写MyImageVeiw 继承ImageVeiw,重写setImageDrawable 方法,看看是否能得到相关信息。关键代码如下:

@Override    public void setImageDrawable(@Nullable Drawable drawable) {        super.setImageDrawable(drawable);        if (drawable instanceof BitmapDrawable) {            Log.d("lxb", "drawable is instanceof BitmapDrawable.");            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();            if (bitmap != null) {                Log.d("lxb", "bitmap's width = " + bitmap.getWidth()                        + " height = " + bitmap.getHeight()                        + " size = " + bitmap.getByteCount() + "b");            }        }    }

运行程序,我们得到如下的日志信息:

hey, 这信息不就我们在第二章时候提到的png的图片信息吗?3,062,208b 换算下,不就是3MB吗? 那如何得到这个3MB的值? 默认Bitmap是以ARGB_8888的格式进行加载的,也就是一个像素用32位(4比特)进行存储,那么4 * 656 * 1167 = 3062208。所以:
ImageViewde的src 设置的图片原本尺寸越大,就越占用内存,和ImageView的大小无关。
所以,对异常UI(基本不显示的),使用ViewStub不比Gone节省内存。

四、 如何ViewStub的使用

上面说了那么多ViewStub的好处,那本章就讲讲如何使用ViewStub吧。
直接上例子,以第一章的例子为例。
将main_layout.xml改写如下:

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity">    <ViewStub        android:id="@+id/front_img_view_stub"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout="@layout/activity_main_img"/>android.support.constraint.ConstraintLayout>

其中 acvitity_main_img.xml 如下:

<?xml version="1.0" encoding="utf-8"?><ImageView xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/front_bg"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:src="@drawable/screen_bg"/>

之后再代码中

private ViewStub mFrontImgViewStub;private ImageView mFrontImg;private void showFrontImg() {    // 通过判断,是的ViewStub仅仅进行一次 inflate    if (mFrontImgViewStub == null) {        mFrontImgViewStub = findViewById(R.id.front_img_view_stub);        mFrontImg = mFrontImgViewStub.inflate().findViewById(R.id.front_bg);    }    // ViewStub inflate 之后,改布局就加载到main_layout中,所以找到子布局的 id 进行操作    if (mFrontImg.getVisibility() != View.VISIBLE) {        mFrontImg.setVisibility(View.VISIBLE);    }}

注:ViewStub 的 inflate 仅仅只能调用一次

或者有人会为,我改如何隐藏该内容呢?简单!!!

private void dismissFrontImg() {    // ViewStub inflate 之后,我们就必现要拿到被加载的layout的相关ID进行操作,而不是ViewStub的id    if (mFrontImg != null) {        mFrontImg.setVisibility(View.GONE);    }}

是不是很简单?So easy !!!

五、使用其他方法实现ViewStub

还有没有其他方法替代ViewStub的功能呢?
这个当然有:1、 直接使用代码编写layout里的内容, 2、既然ViewStub是通过inflate进行加载的,我们也可以直接使用不是吗?

1 直接使用代码编写layout里的内容:

private ViewGroup mViewContainer;private void showFrontImgByCode() {    if (mViewContainer == null) {        mViewContainer = findViewById(R.id.view_container);    }    ImageView fontImg = new ImageView(this);    fontImg.setImageResource(R.drawable.screen_bg);    ViewGroup.LayoutParams layoutParams =            new ViewGroup.LayoutParams(                    ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.MATCH_PARENT);    mViewContainer.addView(fontImg, layoutParams);}

恩,一个ImageView还是容易搞定的,但是,一旦布局复杂,那代码量可不是一般的复杂,而且容易出错。

2、使用inflate进行加载

private void showFrontImgByInflate() {   // 获取根view    if (mViewContainer == null) {        mViewContainer = findViewById(R.id.view_container);    }    View view = getLayoutInflater().inflate(R.layout.activity_main_img, mViewContainer);    // view.findViewById() 获取里面的子View}

恩,也很简单哇。好像也简单的样子。个人感觉使用ViewStub在布局文件中进行标记,比直接在代码中使用infalte更加的增加可读性。

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. Android进程内存查看
  3. 每天学习一个Android中的常用框架——15.RxJava
  4. android 调用 python
  5. Kotlin概述之Kotlin for Android
  6. 实现Android(安卓)动态加载APK(Fragment or Activity实现)
  7. 两个Activity之间跳转问题之activity的四种加载模式
  8. Android(安卓)Framebuff 分析
  9. Android学习系列--App调试的几个命令实践

随机推荐

  1. 灵犀告诉你5个小玩意证明你和安卓是真爱
  2. Android日志系统Logcat源代码简要分析
  3. 如何学习Android开发编程-初学者的5个步
  4. 在你的android设备运行java web应用程序
  5. Android(安卓)BaiduAi 人脸识别 集成教学
  6. android 左右切换对话框 dialog
  7. 开发一个android app需要的技术
  8. 一个使用FFmpeg库读取3gp视频的例子-Andr
  9. Android客户端与PC服务器实现Socket通信
  10. Android(安卓)资源(resource)学习小结