27
2017
09

Android 布局优化-ViewStub 视图

ViewStub 是一个轻量级的View,View 的宽高为0,不绘制任何东西,只是占一个位置而已。

优势
在需要的时候才加载View,这样实现了View的延迟加载,避免资源的浪费,减少渲染时间, 因此可以提高页面的加载速度。

不足
ViewStub所要替代的layout文件中不能有标签,
ViewStub在加载完后会被移除,或者说是被加载进来的layout替换掉了

用法

<ViewStub  android:id="@+id/view_stub" android:layout_width="match_parent" android:layout_height="match_parent" android:layout="@layout/stub_view_container"/>

用ViewStub加载layout文件时,可以调用 viewStub.setVisibility(View.VISIBLE) 或者 viewStub.inflate()
一旦ViewStub visible/inflated,则ViewStub将从视图框架中移除,其id view_stub 也会失效

实例

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

    <TextView  android:id="@+id/text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:layout_marginTop="10dp" android:text="显示ViewStub"/>


    <ViewStub  android:id="@+id/view_stub" android:inflatedId="@+id/stub_new" android:layout_width="match_parent" android:layout_height="match_parent" android:layout="@layout/stub_view_container"/>

</LinearLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/stub_old"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">

    <TextView
        android:id="@+id/stub_container1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:text="Stub Container1"/>


    <TextView
        android:id="@+id/stub_container2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:layout_below="@+id/stub_container1"
        android:text="Stub Container2"/>

</RelativeLayout>
public class MainActivity extends AppCompatActivity {

    public static String TAG = "OWEN";

    ViewStub viewStub;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewStub = (ViewStub) findViewById(R.id.view_stub);
        Log.e(TAG, "viewStub: " + findViewById(R.id.view_stub));
        Log.e(TAG, "layout: " + findViewById(R.id.stub_new));


        findViewById(R.id.text_view).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewStub.inflate();
                //viewStub.setVisibility(View.VISIBLE);
                Log.e(TAG, "viewStub: " + viewStub);
                Log.e(TAG, "find viewStub: " + findViewById(R.id.view_stub));
            }
        });
    }
}

打印结果

09-27 11:49:39.414 2275-2275/com.owen.provider E/OWEN: viewStub: android.view.ViewStub{420bc4a8 G.E..... ......I. 0,0-0,0 #7f0c0062}
09-27 11:49:39.414 2275-2275/com.owen.provider E/OWEN: layout: null
09-27 11:49:49.044 2275-2275/com.owen.provider E/OWEN: viewStub: android.view.ViewStub{420bc4a8 G.E..... ......I. 0,0-0,0 #7f0c0062}
09-27 11:49:49.044 2275-2275/com.owen.provider E/OWEN: find viewStub: null

通过使用HeirachyView查看布局的变化

加载前布局

这里写图片描述

加载后布局

这里写图片描述

源码分析

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);  // 获取InflatedId
    mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);      // 获取要加载布局的id
    mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    a.recycle();

    setVisibility(GONE);    //设置不可见
    setWillNotDraw(true);    // 设置不绘制内容
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(0, 0); // 设置布局的宽高都是0
}
//加载布标布局
/** * Inflates the layout resource identified by {@link #getLayoutResource()} * and replaces this StubbedView in its parent by the inflated layout resource. * * @return The inflated layout resource. * */
public View inflate() {
    final ViewParent viewParent = getParent(); // 通过getParent来获取View的父视图

    if (viewParent != null && viewParent instanceof ViewGroup) {  // 父布局是否存在并且是ViewGroup类型
        if (mLayoutResource != 0) {
            final ViewGroup parent = (ViewGroup) viewParent; //转成ViewGroup类型
            final LayoutInflater factory;
            if (mInflater != null) {
                factory = mInflater; 
            } else {
                factory = LayoutInflater.from(mContext);
            }
            // 加载布标Id
            final View view = factory.inflate(mLayoutResource, parent,
                    false);

            if (mInflatedId != NO_ID) {
                view.setId(mInflatedId);  //设置布标布局根布局的Id
            }

            final int index = parent.indexOfChild(this);
            parent.removeViewInLayout(this); //将View stub 布局从根布局中移出

            final ViewGroup.LayoutParams layoutParams = getLayoutParams(); 
            if (layoutParams != null) { //判断ViewStub 是否设置了布局参数, 然后将布局添加到父布局中
                parent.addView(view, index, layoutParams);
            } else {
                parent.addView(view, index);
            }

            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");
    }
}
上一篇:Android 数据库升级数据迁移以及数据库导入 下一篇:Linux下pthread