都这时候了,还不懂优化技术?——ViewStub为什么可以帮我们优化响应速度?

Hello,All,我是来自58同城的一名Android开发工程师,在58集团从事APP的开发工作。

欢迎为本篇文章点赞,评论

PS:关注,私信我,帮你内推58,常年招聘前端,移动端,后端,算法。

也欢迎关注我的公众号,在这里可以找到我,同时,这里会不定期地推送一些时下最热门的技术文章和互联网行业工作心路历程

                                                                                 


ViewStub作为一种重要的AndroidUI的优化技术,可以帮助我们实现view的懒加载,不同于INVISIBLE属性,使用ViewStub加载的View在初始化阶段是不进行layout文件加载的,这样也就提高了View的加载速度和界面响应速度。

  •  ViewStub的正确打开方式

相信很多朋友都听说过ViewStub,但是还从来没有用过。正好在最近的技术排期中我用到了它,下面就把代码贴一下,实际上,使用它还是非常方便的,总共分三步:

首先,在需要inflate的layout文件中进行声明

<ViewStub
    android:id="@+id/viewStub"
    android:layout="@layout/layout_for_inflate"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

其中的layout就是你希望之星inflate方法之后显示的布局文件名。

然后,在你的代码中使用findViewById的形式,进行ViewStub的初始化

viewStub = (ViewStub) findViewById(R.id.viewStub);

之后,在需要viewStub的时候执行

viewStub.inflate();

或者

viewStub.setVisibility(View.VISIBLE);

这样,一个完整的布局优化和加载过程就完成了。

  • 使用LinearLayout、RelativeLayout中visibility=true有什么缺点?

众所周知,在我们常用的布局如LinearLayout、RelativeLayout中,都会对布局中的View进行绘制,其中,LinearLayout会在onMeasure的过程中根据设置的布局方向不同进行一次绘制:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

而RelativeLayout则会进行横向和纵向的两次分别Measure

横向:

if (isWrapContentWidth) {
    // Width already has left padding in it since it was calculated by looking at
    // the right of each child view
    width += mPaddingRight;
    if (mLayoutParams != null && mLayoutParams.width >= 0) {
        width = Math.max(width, mLayoutParams.width);
    }
    width = Math.max(width, getSuggestedMinimumWidth());
    width = resolveSize(width, widthMeasureSpec);

纵向:

if (isWrapContentHeight) {
    // Height already has top padding in it since it was calculated by looking at
    // the bottom of each child view
    height += mPaddingBottom;
    if (mLayoutParams != null && mLayoutParams.height >= 0) {
        height = Math.max(height, mLayoutParams.height);
    }
    height = Math.max(height, getSuggestedMinimumHeight());
    height = resolveSize(height, heightMeasureSpec);

其中,横向和纵向每次都会在每次初始化加载时候对其中的子View分别进行测量,可想而知,这样的工作量越多,对UI的加载性能影响越大。

  • ViewStub好在哪里?为什么可以实现懒加载?

ViewStub一个最重要的功能就是可以帮助我们来实现懒加载,下面我们就来从源码的角度,来看看为什么ViewStub在inflate或者setVisibility为VISIBLE之前是不会被绘制的?

在inflate里面我们可以看到这样的一行代码:

final View view = inflateViewNoAdd(parent);

让我们点进去看看里面做了些什么

private View inflateViewNoAdd(ViewGroup parent) {
    final LayoutInflater factory;
    if (mInflater != null) {
        factory = mInflater;
    } else {
        factory = LayoutInflater.from(mContext);
    }
    final View view = factory.inflate(mLayoutResource, parent, false);

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

在这里,把ViewStub的父View,也就是根节点传了进来,作为factory来执行了inflate的过程,看到这里,相信很多经常使用Frament的同学会有些似曾相识的感觉,没错,在Fragment的onCreateView里也用到了inflate方法。

同时,在我们的工作中,如果想在任意位置添加一个view的时候,我们也会通过view.inflate的方法来进行layout的加载过程。

紧接着,我们看到,调用了一个replaceSelfWithView的方法

private void replaceSelfWithView(View view, ViewGroup parent) {
    final int index = parent.indexOfChild(this);
    parent.removeViewInLayout(this);
    final ViewGroup.LayoutParams layoutParams = getLayoutParams();
    if (layoutParams != null) {
        parent.addView(view, index, layoutParams);
    } else {
        parent.addView(view, index);
    }
}

在这个方法里,我们能清晰地看到,传入了ViewStub的view引用和ViewStub的父View。然后,显示拿到了当前的ViewStub在父view中的index也就是位置属性,然后将这个ViewStub从父view里面进行了移除,然后拿到了ViewStub里面的布局参数并且根据这个布局参数将这个view add进了父View中,并在addView中调用了invalidate从而进行了UI的刷新,展示。

  • 总结:

其实通过ViewStub的整个渲染过程我们不难发现,其实他就是利用了ViewGroup的inflate方法来进行layout的加载,从而实现了在UI的懒加载。

同时,我们也可以推断,在某些场景下,ViewStub是可以替代view.inflate的。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章