Skip to content

Commit 71bd6a6

Browse files
authored
add ViewStub 源码解析.md
1 parent 8a90368 commit 71bd6a6

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

article/ViewStub 源码解析.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# ViewStub
2+
3+
ViewStub 是一个看不见的,没有大小,不占布局位置的 View,可以用来懒加载布局。当 ViewStub 变得可见或 ```inflate()``` 的时候,布局就会被加载(替换 ViewStub)。因此,ViewStub 一直存在于视图层次结构中直到调用了 ```setVisibility(int)``````inflate()```
4+
5+
我们先来看看构造方法:
6+
7+
```java
8+
public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
9+
super(context);
10+
11+
final TypedArray a = context.obtainStyledAttributes(attrs,
12+
R.styleable.ViewStub, defStyleAttr, defStyleRes);
13+
// 要被加载的布局 Id
14+
mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
15+
// 要被加载的布局
16+
mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
17+
// ViewStub 的 Id
18+
mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
19+
a.recycle();
20+
21+
// 初始状态为 GONE
22+
setVisibility(GONE);
23+
// 设置为不会绘制
24+
setWillNotDraw(true);
25+
}
26+
```
27+
28+
接下来就看看关键的方法,ViewStub 很轻量,代码真的很少,很值得去看看具体实现,花不了多少时间。
29+
30+
```java
31+
// 复写了 setVisibility(int) 方法
32+
@Override
33+
@android.view.RemotableViewMethod
34+
public void setVisibility(int visibility) {
35+
// private WeakReference<View> mInflatedViewRef;
36+
// mInflatedViewRef 是对布局的弱引用
37+
if (mInflatedViewRef != null) {
38+
// 如果不为 null,就拿到懒加载的 View
39+
View view = mInflatedViewRef.get();
40+
if (view != null) {
41+
// 然后就直接对 View 进行 setVisibility 操作
42+
view.setVisibility(visibility);
43+
} else {
44+
// 如果为 null,就抛出异常
45+
throw new IllegalStateException("setVisibility called on un-referenced view");
46+
}
47+
} else {
48+
super.setVisibility(visibility);
49+
// 之前说过,setVisibility(int) 也可以进行加载布局
50+
if (visibility == VISIBLE || visibility == INVISIBLE) {
51+
// 因为在这里调用了 inflate()
52+
inflate();
53+
}
54+
}
55+
}
56+
```
57+
58+
inflate() 是关键的加载实现
59+
60+
```java
61+
public View inflate() {
62+
// 获取父视图
63+
final ViewParent viewParent = getParent();
64+
65+
if (viewParent != null && viewParent instanceof ViewGroup) {
66+
// 如果没有指定布局,就会抛出异常
67+
if (mLayoutResource != 0) {
68+
// viewParent 需为 ViewGroup
69+
final ViewGroup parent = (ViewGroup) viewParent;
70+
final LayoutInflater factory;
71+
if (mInflater != null) {
72+
factory = mInflater;
73+
} else {
74+
// 如果没有指定 LayoutInflater
75+
factory = LayoutInflater.from(mContext);
76+
}
77+
// 获取布局
78+
final View view = factory.inflate(mLayoutResource, parent,
79+
false);
80+
// 为 view 设置 Id
81+
if (mInflatedId != NO_ID) {
82+
view.setId(mInflatedId);
83+
}
84+
// 计算出 ViewStub 在 parent 中的位置
85+
final int index = parent.indexOfChild(this);
86+
// 把 ViewStub 从 parent 中移除
87+
parent.removeViewInLayout(this);
88+
89+
// 接下来就是把 view 加到 parent 的 index 位置中
90+
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
91+
if (layoutParams != null) {
92+
// 如果 ViewStub 的 layoutParams 不为空
93+
// 就设置给 view
94+
parent.addView(view, index, layoutParams);
95+
} else {
96+
parent.addView(view, index);
97+
}
98+
99+
// mInflatedViewRef 就是在这里对 view 进行了弱引用
100+
mInflatedViewRef = new WeakReference<View>(view);
101+
102+
if (mInflateListener != null) {
103+
// 回调
104+
mInflateListener.onInflate(this, view);
105+
}
106+
107+
return view;
108+
} else {
109+
throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
110+
}
111+
} else {
112+
throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
113+
}
114+
}
115+
```
116+
117+
好了,主要的都分析完了,知道原理之后就可以自己动手写一个加强版的 ViewStub 了,例如我以前写的一个 [StateView](https://github.com/nukc/StateView)

0 commit comments

Comments
 (0)