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的。