ViewStub源码研究
来源:互联网 发布:苹果手机频谱软件 编辑:程序博客网 时间:2024/06/07 23:57
周末在家,没有办法写demo,当然也没办法测试,只能通过源码直接分析。网上找了一个源码库,分享给大家
链接:http://repository.grepcode.com/java/ext/
ViewStub源码链接:http://www.grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/view/ViewStub.java
简介
ViewStub是一个用来延迟初始化的View的类。适用于那些Views布局复杂,或者加载比较耗时的情况,延迟初始化可以优化用户体验。
官方解释的理解
ViewStub是不可见的,0像素View,被用来在运行时推迟填充layout资源。
ViewStub调用setVisibility(View.VISIBLE)或inflate()后,会触发inflated layout被初始化。,ViewStub本身会被removed掉,inflated Layout会添加到parent layout中。
注意,inflatedlayout会使用ViewStub的layoutparameters。被remove掉后,ViewStub就没了。后续就得使用inflated ID。
ViewStub的xml布局范例
<ViewStubandroid:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
android:layout_width="120dip"
android:layout_height="40dip"/>
解析前通过android:id->stub可以find到ViewStub,inflate()之后通过android:inflatedId->subTree也可以找到inflatedlayout。
换个方式,还可以这样。
ViewStub stub = (ViewStub) findViewById(R.id.stub);
View inflated = stub.inflate();
Inflatedlayout不需要通过findViewById也可以直接get到View。
这是官方推荐的获取方式。我也觉得更靠谱,避免了一些不必要的逻辑问题。
源码解析
变量定义
private int mLayoutResource = 0;
private int mInflatedId;
private WeakReference<View>mInflatedViewRef;
private LayoutInflater mInflater;
private OnInflateListener mInflateListener;
这里需要看下红字标明的部分。
ViewStub结构体
public ViewStub(Context context, intlayoutResource) {
mLayoutResource = layoutResource;
initialize(context);
}
public ViewStub(Context context,AttributeSet attrs) {
this(context, attrs, 0);
}
@SuppressWarnings({"UnusedDeclaration"})
public ViewStub(Context context,AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ViewStub(Context context,AttributeSet attrs, int defStyleAttr, int defStyleRes) {
TypedArray a = context.obtainStyledAttributes(
attrs,com.android.internal.R.styleable.ViewStub, defStyleAttr, defStyleRes);
mInflatedId =a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
mLayoutResource =a.getResourceId(R.styleable.ViewStub_layout, 0);
a.recycle();
a = context.obtainStyledAttributes(
attrs,com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
mID =a.getResourceId(R.styleable.View_id, NO_ID);
a.recycle();
initialize(context);
}
除了mInflatedId和mLayoutResource的赋值,主要看initialize(context).
Initialize(context)
private void initialize(Context context) {
mContext = context;
setVisibility(GONE);
setWillNotDraw(true);
}
看起来一切正常。setWillNotDraw(true)你懂的,主要看setVisibility(GONE)。
setVisibility(GONE)
@Override
@android.view.RemotableViewMethod
public void setVisibility(int visibility) {
if (mInflatedViewRef != null) {
View view = mInflatedViewRef.get();
if (view != null) {
view.setVisibility(visibility);
} else {
throw newIllegalStateException("setVisibility called on un-referenced view");
}
} else {
super.setVisibility(visibility);
if(visibility == VISIBLE || visibility == INVISIBLE) {
inflate();
}
}
}
这里可以看到setVisibility被ViewStub重写。
若mInflatedViewRef保存了inflatedView的弱引用,如果什么时候这个弱引用get到一个null是要抛出IllegalStateException的。那么在使用的时候就需要注意了,这是可能会挂的。
倘若没有被inflated过,那么mInflatedViewRef肯定是null。看红色标红地方,setVisibility只有在View.GONE才是不会调用inflated的,其他情况都会启动 inflate()。
继续看源码
public int getInflatedId()
@android.view.RemotableViewMethod
public void setInflatedId(int inflatedId)
public int getLayoutResource()
@android.view.RemotableViewMethod
public void setLayoutResource(int layoutResource)
public void setLayoutInflater(LayoutInflater inflater)
public LayoutInflater getLayoutInflater()
@Override
protected void onMeasure(int widthMeasureSpec, intheightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
}
@Override
protected void dispatchDraw(Canvas canvas) {
}
这里可以看到ViewStub本身就是空白的。
另一个重头函数inflate()
public View inflate() {
final ViewParent viewParent =getParent();
if (viewParent != null && viewParentinstanceof ViewGroup) {
if (mLayoutResource != 0) {
final ViewGroup parent =(ViewGroup) viewParent;
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);
}
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);
}
mInflatedViewRef = newWeakReference<View>(view);
if (mInflateListener != null) {
mInflateListener.onInflate(this, view);
}
return view;
} else {
thrownew IllegalArgumentException("ViewStub must have a validlayoutResource");
}
} else {
throw new IllegalStateException("ViewStubmust have a non-null ViewGroup viewParent");
}
}
注意看标红的code。
第一个 if(viewParent != null && viewParent instanceof ViewGroup)
说明--->ViewStub不能单独使用,必须作为ViewGroup的二级layout。
第二个 if(mLayoutResource != 0)
说明--->ViewStub如果没有inflate layout定义,那就根本没有存在的价值,直接报错了。IllegalArgument。非法主题。类型都说的很直白了。
第三个 if(mInflatedId != NO_ID) {
view.setId(mInflatedId);
}
说明--->如果没有定义android:inflatedId,那么inflated layout是不会被赋值到inflate出来的Views的
第四个 parent.removeViewInLayout(this);
说明--->ViewStub在inflate之后,会先被remove
第五个 final ViewGroup.LayoutParamslayoutParams = getLayoutParams();
if(layoutParams != null) {
parent.addView(view, index, layoutParams);
}
说明--->inflatedlayout被赋予了ViewStub的layoutparameters
第六个 mInflatedViewRef= new WeakReference<View>(view);
说明--->inflatedlayout被封装在了WeakReference,各位朋友,弱引用的利弊要细细品味。既然是弱引用一些必要的判断就必不可少了。
第七个 mInflateListener.onInflate(this,view);
说明--->inflate完成后,会通过onInflate(this,view)。讲ViewStub和inflated view回调出来,通知ViewStub所在的Activity实例
小结
1. ViewStub必须作为ViewGroup的二级view。
2. ViewStub必须要有inflateLayout
3. ViewStub的延迟加载原理是将必要的id保存到新view,inflate新new后,先remove ViewStub自身,再讲新View add到parent上。
4. ViewStub利用了同样的layout parameters,所以不会让你感到闪烁,如果有这种感觉,请查一下自己的inflateLayout
5. ViewStub利用了WeakReference保存了inflated View,弱引用是把双刃剑,在使用ViewStub时要小心
- ViewStub源码研究
- ViewStub 源码实现
- ViewStub 源码简解
- ViewStub
- ViewStub
- ViewStub
- ViewStub
- viewstub
- ViewStub
- ViewStub
- ViewStub?
- ViewStub
- ViewStub
- Android_Merge、Include、ViewStub使用与源码分析
- ViewStub、include、merge使用与源码分析
- ViewStub使用详解——从源码看ViewStub的使用
- Android抽象布局——include、merge 、ViewStub(附源码)
- 从源码角度分析ViewStub 疑问与原理
- 在__init__.py文件中import语句的作用
- JSP中文乱码问题终极解决方案(下)
- leetcode 10 Regular Expression Matching(简单正则表达式匹配)
- iOS 10.2 越狱更新到yalu102 beta7,很稳定,很多注意事项及修复ssh和scp连接教程
- 使用LVS 实现负载均衡的原理。
- ViewStub源码研究
- LeetCode -- Arranging Coins
- 输入一个链表,从尾到头打印链表每个节点的值。
- 常见前端面试题及答案(上)
- 1094. The Largest Generation (25)
- LeetCode -- First Unique Character in a String
- IPython Notebook使用实例
- 谷歌学术辅助科研
- servelt2/3 config