轉載請註明出處:http://blog.csdn.net/hjf_huangjinfu/article/details/78573779
概述
本文基於api level 26 的代碼,簡單描述一下 RelativeLayout 的內部工作原理。1、先來看一個例子
下面我們會根據源代碼和這個例子,來說明一下內部工作原理。一個簡單的佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/A"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="A"/>
<TextView
android:id="@+id/B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/D"
android:layout_toRightOf="@id/A"
android:gravity="center"
android:padding="10dp"
android:text="B"/>
<TextView
android:id="@+id/C"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/B"
android:gravity="center"
android:padding="10dp"
android:text="C"/>
<TextView
android:id="@id/D"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="300dp"
android:layout_toRightOf="@id/A"
android:gravity="center"
android:padding="10dp"
android:text="D"/>
</RelativeLayout>
A沒有任何依賴,放置在屏幕的左上角。
D依賴於A,放置在A的右邊,D還依賴於parent,與parent對齊(爲了方便顯示,加了margin,不影響流程分析)。
B依賴於A、D,放置在A的右邊,放置在D的上邊。
C依賴於B,放置在B的右邊。
2、約束規則
RelativeLayout 爲它內部的每一個子View都生成一個約束規則(位置關係),約束規則有兩大類:相對規則,也就是說相對於它的兄弟View的約束規則 取值爲View的id(int型),比如:
android:layout_toRightOf="@id/A"
絕對規則,也就是相對於其父佈局的約束規則,取值範圍爲【true / false】比如:android:layout_alignParentBottom="true"
每一位的值,有三種情況:
大於0的數:代表着相對規則的值,也就是所依賴View的Id。
public static final int LEFT_OF = 0;
public static final int RIGHT_OF = 1;
public static final int ABOVE = 2;
public static final int BELOW = 3;
public static final int ALIGN_BASELINE = 4;
public static final int ALIGN_LEFT = 5;
public static final int ALIGN_TOP = 6;
public static final int ALIGN_RIGHT = 7;
public static final int ALIGN_BOTTOM = 8;
public static final int ALIGN_PARENT_LEFT = 9;
public static final int ALIGN_PARENT_TOP = 10;
public static final int ALIGN_PARENT_RIGHT = 11;
public static final int ALIGN_PARENT_BOTTOM = 12;
public static final int CENTER_IN_PARENT = 13;
public static final int CENTER_HORIZONTAL = 14;
public static final int CENTER_VERTICAL = 15;
public static final int START_OF = 16;
public static final int END_OF = 17;
public static final int ALIGN_START = 18;
public static final int ALIGN_END = 19;
public static final int ALIGN_PARENT_START = 20;
public static final int ALIGN_PARENT_END = 21;
除了約束規則,還有約束方向,也就是說是在哪個方向上面的約束,主要有水平方向和豎直方向。上面的例子中,他們的約束關係是這樣的:
mRules[12] = mRules[ALIGN_PARENT_BOTTOM] = -1。
3、工作過程
private View[] mSortedHorizontalChildren;
private View[] mSortedVerticalChildren;
/** 根據傳進來的規則過濾器,來找到所有的沒有相對規則的View
規則過濾器,系統給了兩大類,分別是 垂直過濾器 和 水平過濾器 ,過濾器中存儲的是mRules值的索引。
private static final int[] RULES_VERTICAL = {
ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
};
private static final int[] RULES_HORIZONTAL = {
LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
};
也就是說,在某一個方向上,當某一個View,它的約束規則(mRules),比如說垂直方向,RULES_VERTICAL 數組中的這些約束,在mRules中都爲0,那麼它就可以成爲這個方向上的Root。
*/
private ArrayDeque<Node> findRoots(int[] rulesFilter) {
final SparseArray<Node> keyNodes = mKeyNodes;
final ArrayList<Node> nodes = mNodes;
final int count = nodes.size();
//清理掉所有的上次計算結果
for (int i = 0; i < count; i++) {
final Node node = nodes.get(i);
node.dependents.clear();
node.dependencies.clear();
}
// 爲每一個View構建它的依賴和被依賴關係
for (int i = 0; i < count; i++) {
//需要分析的View(Node對View做了包裹)
final Node node = nodes.get(i);
//取出View的佈局參數,主要就是拿到mRules。
final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
final int[] rules = layoutParams.mRules;
final int rulesCount = rulesFilter.length;
//檢測mRules中,指定的規則過濾器中的規則,有沒有被設置了值。
for (int j = 0; j < rulesCount; j++) {
final int rule = rules[rulesFilter[j]];
//rule大於0,說明是一個View的Id,說明這個約束規則依賴於另外一個Id爲rule的View。就說明該View依賴於Id爲rule的View,依賴規則爲rulesFilter[j]。
if (rule > 0) {
//找到Id爲rule的View,也就是被該View依賴的View
final Node dependency = keyNodes.get(rule);
// 跳過未知依賴和自我依賴
if (dependency == null || dependency == node) {
continue;
}
//記錄下Id爲rule的View的被依賴關係
dependency.dependents.put(node, this);
//記錄下當前View的依賴關係
node.dependencies.put(rule, dependency);
}
}
}
final ArrayDeque<Node> roots = mRoots;
roots.clear();
//找到所有相對依賴關係爲空的View,他們就是Roots
for (int i = 0; i < count; i++) {
final Node node = nodes.get(i);
if (node.dependencies.size() == 0) roots.addLast(node);
}
return roots;
}
/**
生成排序後的數組,排序規則如下,如果C依賴於A,而A依賴於B,那麼排序就爲 B -> A -> C。
*/
void getSortedViews(View[] sorted, int... rules) {
//查找所有Roots
final ArrayDeque<Node> roots = findRoots(rules);
int index = 0;
Node node;
//從Roots的隊列尾部取出View
while ((node = roots.pollLast()) != null) {
final View view = node.view;
final int key = view.getId();
//放置到排序數組的前面
sorted[index++] = view;
//拿到所有依賴於該View的View
final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
final int count = dependents.size();
for (int i = 0; i < count; i++) {
//拿到依賴於該View的View
final Node dependent = dependents.keyAt(i);
final SparseArray<Node> dependencies = dependent.dependencies;
//移除對該View的依賴
dependencies.remove(key);
//如果對該View依賴的View,已經沒有其他依賴關係了,那麼他也就成爲一個新的Roots
if (dependencies.size() == 0) {
roots.add(dependent);
}
}
}
//循環依賴的檢測,非法的
if (index < sorted.length) {
throw new IllegalStateException("Circular dependencies cannot exist"
+ " in RelativeLayout");
}
}
上述例子中,這兩個數組最後的值爲: