性能优化之布局优化篇一 使用ViewStub视图
2016-09-02 19:28
411 查看
一、概述
ViewStub是一个不显示且不占用布局空间的视图。ViewStub需要指定一个布局layout,在ViewStub初始化时,其指定的layout并未初始化。只有当ViewStub的setVisibility(VISIBLE/INVISIBLE)或inflate()方法被调用,ViewStub所指向的布局layout才会实例化,该layout会使用ViewStub的布局参数LayoutParam,被添加到ViewStub的父视图中来替代ViewStub,即ViewStub将不在布局中存在。
ViewStub类官方文档:
https://developer.android.com/reference/android/view/ViewStub.html
二、使用场景
在开发中,会遇到这样的情况,需要在运行时动态的决定显示某个视图。通常的做法是,将可能会用到的所有视图都添加到布局中,之后在代码中通过View.VISIBILITY和View.GONE来改变视图的可见性。即使在xml布局中为视图设置了android:visibility="gone",该视图仍然会被实例化,这样做带来的缺点是增加了视图加载的时间和内存的消耗。
ViewStub的一个典型的使用案例,当我们启动一个页面,如果请求到数据就显示内容视图,遇到网络出错时显示网络异常视图。
添加网络异常视图net_error_view.xml。
Activity的布局分两部分,一个内容视图,一个网络异常视图。网络异常视图使用ViewStub,并将layout指定为上面的net_error_view。
在Activity中,如果需要显示网络异常视图,使用如下代码。
当ViewStub的inflate()方法被调用时,其指定的@layout/net_error_view被实例化并显示出来。此时,ViewStub的android:inflatedId即inflatedview,就成为net_error_view视图根布局的id。这里我们将网络异常视图对象保存到成员变量netErrorView中,之后可以直接操作netErrorView,不用再调用findViewById()。
三、源码分析
ViewStub继承自View类,所以本质上还是一个View。
ViewStub视图在加载时,为何没有显示,且指定的layout没有被初始化,原因在于ViewStub重写了View类的onMeasure()和draw()方法。在onMeasure()方法中,调用setMeasuredDimension,传递的参数measuredWidth和measuredHeight都是0。draw()方法的内部实现被置空,也就不会绘制了。
ViewStub除了拥有父类View的属性外,新增了android:inflatedId和android:layout,这两个属性在构造方法中完成初始化。
当ViewStub的inflate()方法被调用时,指定的layout被实例化显示出来,并返回。我们来看看inflate()方法的实现。
ViewStub的setVisibility()方法被调用时,指定的layout同样也可以被实例化。ViewStub对View中的setVisibility()方法进行了重写。
所以,当ViewStub的inflate()方法被调用,或setVisibility()方法被调用并传入参数VISIBLE或INVISIBLE,ViewStub所指向的布局layout会被实例化,并添加到ViewStub的父视图中显示出来。
ViewStub是一个不显示且不占用布局空间的视图。ViewStub需要指定一个布局layout,在ViewStub初始化时,其指定的layout并未初始化。只有当ViewStub的setVisibility(VISIBLE/INVISIBLE)或inflate()方法被调用,ViewStub所指向的布局layout才会实例化,该layout会使用ViewStub的布局参数LayoutParam,被添加到ViewStub的父视图中来替代ViewStub,即ViewStub将不在布局中存在。
ViewStub类官方文档:
https://developer.android.com/reference/android/view/ViewStub.html
二、使用场景
在开发中,会遇到这样的情况,需要在运行时动态的决定显示某个视图。通常的做法是,将可能会用到的所有视图都添加到布局中,之后在代码中通过View.VISIBILITY和View.GONE来改变视图的可见性。即使在xml布局中为视图设置了android:visibility="gone",该视图仍然会被实例化,这样做带来的缺点是增加了视图加载的时间和内存的消耗。
ViewStub的一个典型的使用案例,当我们启动一个页面,如果请求到数据就显示内容视图,遇到网络出错时显示网络异常视图。
添加网络异常视图net_error_view.xml。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/net_icon"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="网络异常\n点击屏幕重新加载" android:textColor="#666666" android:textSize="16sp"/> </LinearLayout>
Activity的布局分两部分,一个内容视图,一个网络异常视图。网络异常视图使用ViewStub,并将layout指定为上面的net_error_view。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff"> <!-- 内容视图 --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> </LinearLayout> <!-- 网络异常视图 --> <ViewStub android:id="@+id/viewstub" android:layout_width="wrap_content" android:layout_height="wrap_content" android:inflatedId="@+id/inflatedview" android:layout="@layout/net_error_view"/> </FrameLayout>
在Activity中,如果需要显示网络异常视图,使用如下代码。
private View netErrorView; ViewStub viewStub = (ViewStub) findViewById(R.id.net_error); netErrorView = viewStub.inflate();或
viewStub.setVisibility(View.VISIBLE); netErrorView = findViewById(R.id.inflatedview);
当ViewStub的inflate()方法被调用时,其指定的@layout/net_error_view被实例化并显示出来。此时,ViewStub的android:inflatedId即inflatedview,就成为net_error_view视图根布局的id。这里我们将网络异常视图对象保存到成员变量netErrorView中,之后可以直接操作netErrorView,不用再调用findViewById()。
三、源码分析
ViewStub继承自View类,所以本质上还是一个View。
public final class ViewStub extends View { }
ViewStub视图在加载时,为何没有显示,且指定的layout没有被初始化,原因在于ViewStub重写了View类的onMeasure()和draw()方法。在onMeasure()方法中,调用setMeasuredDimension,传递的参数measuredWidth和measuredHeight都是0。draw()方法的内部实现被置空,也就不会绘制了。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(0, 0); } @Override public void draw(Canvas canvas) { }
ViewStub除了拥有父类View的属性外,新增了android:inflatedId和android:layout,这两个属性在构造方法中完成初始化。
private int mInflatedId; private int mLayoutResource; public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStub, defStyleAttr, defStyleRes); mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID); mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0); mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID); a.recycle(); setVisibility(GONE); setWillNotDraw(true); }获取到属性inflatedId和layout的值后分别赋值给成员变量mInflatedId和mLayoutResource。并在最后调用了setWillNotDraw(true),只有View就不会绘制自己。
当ViewStub的inflate()方法被调用时,指定的layout被实例化显示出来,并返回。我们来看看inflate()方法的实现。
public View inflate() { final ViewParent viewParent = getParent(); if (viewParent != null && viewParent instanceof ViewGroup) { if (mLayoutResource != 0) { // 获取ViewStub的父视图,强转为ViewGroup类型 final ViewGroup parent = (ViewGroup) viewParent; // 获取LayoutInflater对象 final LayoutInflater factory; if (mInflater != null) { factory = mInflater; } else { factory = LayoutInflater.from(mContext); } // 实例化mLayoutResource,赋值给view变量 final View view = factory.inflate(mLayoutResource, parent, false); // 如果设置了mInflatedId,赋值给view的id if (mInflatedId != NO_ID) { view.setId(mInflatedId); } // 移除ViewStub自身 final int index = parent.indexOfChild(this); parent.removeViewInLayout(this); // getLayoutParams()获取的是ViewStub的布局参数 final ViewGroup.LayoutParams layoutParams = getLayoutParams(); // 把view添加到ViewStub的父视图中 if (layoutParams != null) { parent.addView(view, index, layoutParams); } else { parent.addView(view, index); } // 把view赋值给成员变量mInflatedViewRef mInflatedViewRef = new WeakReference<View>(view); if (mInflateListener != null) { mInflateListener.onInflate(this, view); } // 最后返回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"); } }首先,使用LayoutInflater将指定的layout实例化赋值给变量view。如果设置了inflatedId,赋值给该view的id属性。然后,从ViewStub的父视图parent中移除ViewStub自身,使用ViewStub的布局参数把view添加到parent中。最后,把view赋值给成员变量mInflatedViewRef,返回view。
ViewStub的setVisibility()方法被调用时,指定的layout同样也可以被实例化。ViewStub对View中的setVisibility()方法进行了重写。
@Override public void setVisibility(int visibility) { if (mInflatedViewRef != null) {// 如果ViewStub的inflate()已经被调用,执行if View view = mInflatedViewRef.get(); // 此时ViewStub已经被inflate(),view已经有值,直接执行view的setVisibility()方法 if (view != null) { view.setVisibility(visibility); } else { throw new IllegalStateException("setVisibility called on un-referenced view"); } } else {// 如果ViewStub的inflate()还未被调用,执行else // 先调用父类View的setVisibility() super.setVisibility(visibility); // 如果传入的参数为VISIBLE或INVISIBLE,执行inflate()方法 if (visibility == VISIBLE || visibility == INVISIBLE) { inflate(); } } }在setVisibility()方法被调用之前,如果ViewStub的inflate()已经调用,此时成员变量mInflatedViewRef即是ViewStub指定的layout对象,直接执行view的setVisibility()方法。如果ViewStub的inflate()未被调用,先将参数传递到父类View的setVisibility(),然后判断传入的参数为VISIBLE或INVISIBLE时,执行inflate()方法,去实例化layout对象。
所以,当ViewStub的inflate()方法被调用,或setVisibility()方法被调用并传入参数VISIBLE或INVISIBLE,ViewStub所指向的布局layout会被实例化,并添加到ViewStub的父视图中显示出来。
相关文章推荐
- Android 性能优化 三 布局优化ViewStub标签的使用
- 【Android Training - Performance】提高显示布局文件的性能[Lesson 3 - 按需载入视图(ViewStub的使用方法)]
- Android 性能优化 三 布局优化ViewStub标签的使用
- Android Training - 提升布局文件的性能(Lesson 3 - 使用viewStub按需载入视图)
- Android布局优化之Merge Include ViewStub使用与源码分析
- Android布局性能优化—从源码角度看ViewStub延迟加载技术
- Android布局优化之ViewStub、include、merge使用与源码分析 .
- Android布局优化几个重要标签的使用include、merge、ViewStub、requestFocus
- Android布局优化之Merge、Include、ViewStub使用与源码分析
- Android视图优化之Merge,ViewStub基本使用
- Android布局优化之ViewStub、include、merge使用与源码分析
- Android性能优化之加载速度优化(一)ViewStub使用
- Android布局优化之ViewStub、include、merge使用与源码分析
- Android布局优化之ViewStub、include、merge使用与源码分析
- Android布局优化之Merge Include ViewStub使用与源码分析
- 使用include、merge、ViewStub优化布局
- Android布局优化之<merge>与<ViewStub>标签使用
- Android-优化UI性能(4)-使用ViewStub
- android--(内存管理、布局性能优化hierarchyviewer工具、viewStub(需要时显示))
- Android布局优化之Merge Include ViewStub使用与源码分析