Android(安卓)使用View Gone 与 ViewStub的区别
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更加的增加可读性。
更多相关文章
- 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
- Android进程内存查看
- 每天学习一个Android中的常用框架——15.RxJava
- android 调用 python
- Kotlin概述之Kotlin for Android
- 实现Android(安卓)动态加载APK(Fragment or Activity实现)
- 两个Activity之间跳转问题之activity的四种加载模式
- Android(安卓)Framebuff 分析
- Android学习系列--App调试的几个命令实践