知识填充#
布局相关#
LayoutInflater#
在绑定Fragment的时候我们会用到LayoutInflater.inflate(), 该方法返回一个View对象,所以我们可以知道该方法的作用就是将xml布局文件加载为View或者ViewGroup对象。而LayoutInflater就是一个总的工具,有多个inflate方法。
获取LayoutInflater#
1
2
3
|
LayoutInflater inflater1 = LayoutInflater.from(this);
LayoutInflater inflater2 = getLayoutInflater();
LayoutInflater inflater3 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
|
后面两种其实走的都是第一种方法。this就是context。
.infalte()方法使用#
.infalte()有多个重载的方法。
1
2
3
4
|
inflate(int resource, ViewGroup root)
inflate(int resource, ViewGroup root, boolean attachToRoot)
inflate(XmlPullParser parser, ViewGroup root)
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
|
看源码可知,四个方法,前三个其实都是调用的第四个。
1
2
|
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
}
|
可以看到第一个参数是一个Xml解析器,其实就是去解析布局的xml文件,这里使用的是Pull解析器。下面来看一下源码。
1
2
3
4
5
6
7
8
9
10
11
12
|
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
|
第一段代码就是去找xml解析器中的开始节点,找到后获得名字,这个name就是xml文件的根节点的name。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
}
|
A. 这段代码首先就是判断了一下根节点是不是merge,如果根节点是merge,又没有传root,attachToRoot又是false就会直接抛异常。所以说如果是merge为根节点的话,则需要传root参数或者设置attachRoot为true。
如果merge节点并且传了root或者attachToRoot设置为true后,则直接进入递归方法rInflate()去继续解析布局,最终返回result。
B. 如果不是merge根节点,大部分的情况。首先调用createViewFromTag创建了个temp,这个temp就是布局根View,后续又是调用递归方法rInflateChildren来完成子节点的View的创建。
C. 值得注意的是在代码的最后有关于
1
2
3
4
5
6
7
8
9
10
11
|
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
|
- root != null && attchToRoot
传进来的xml文件布局解析完成后会作为子View添加到root中,最终返回root。
- root == null && !attchToRoot
传进来的xml文件布局解析完成后成为一个View直接返回,注意看代码中,返回并没有返回params,也就是说布局根View的android:layout_xxx属性会被忽略,即android:layout_xx属性只有依附在某个ViewGroup中才能生效。
- root != null && !attchToRoot
传进来的xml文件会被加载成为一个View直接返回。布局根View中的android:layout_xx会生效。
最后再来看一下加载xml布局的原理,我们可以看到有递归函数 rInflate 来递归完成整体的加载。其中每一步递归都是获取节点的名字,然后创建对应的实例,生成View,并且将该View添加到它的父View上。最终就返回了一个完整的View。
Shape#
对于shape标签,如果有多个布局文件拥有同一个背景的话,可以考虑写个shape标签的xml来进行代码复用。相对于使用png图片来说,使用shape可以减少安装包的大小,而且能够更好的适配不同的手机。
- :shape可以指定形状,一共有四个形状,矩形: rectangle, 椭圆: oval, 线: line, 圆环: ring
1
|
android:shape=["rectangle" | "oval" | "line" | "ring"] >
|
- :绘制图形的大小
1
2
3
|
<size
android:width="integer"
android:height="integer" />
|
- : 填充颜色
1
2
|
<solid
android:color="color" />
|
- :圆角大小
1
2
3
4
5
6
|
<corners
android:radius="integer"
android:topLeftRadius="integer"
android:topRightRadius="integer"
android:bottomLeftRadius="integer"
android:bottomRightRadius="integer" />
|
- : 描边的颜色及宽度
1
2
3
4
5
|
<stroke
android:width="integer"
android:color="color"
android:dashWidth="integer"
android:dashGap="integer" />
|
- : 渐变背景颜色
1
2
3
4
5
6
7
8
9
10
|
<gradient
android:angle="integer"
android:centerX="integer"
android:centerY="integer"
android:centerColor="integer"
android:endColor="color"
android:gradientRadius="integer"
android:startColor="color"
android:type=["linear" | "radial" | "sweep"]
android:useLevel=["true" | "false"] />
|
TextWatcher#
顾名思义,文件监听器。就是用来监听TextView的变化,一共有3个重写方法。
1
2
3
4
5
6
7
8
|
// 在文本变化之前 start开始的位置,count变化的字符长度 after变化后的位置
public void beforeTextChanged(CharSequence s, int start,
int count, int after);
// 文本变化
public void onTextChanged(CharSequence s, int start, int before, int count);
// 文本变化之后调用 s为文本变化后的结果
public void afterTextChanged(Editable s);
|