您的位置:首页 > 其它

RelativeLayout 原理浅析

2017-11-19 15:13 567 查看

转载请注明出处: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>

很简单,界面如下图所示:



这里有4个控件,暂且我们叫它们为A、B、C、D。

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"


这些规则统一保存在View对象的RelativeLayout.LayoutParams 对象中,RelativeLayout.LayoutParams 对象使用一个int[] mRules 数组来存储约束规则,mRules数据逻辑是这样的,每一位对应一个约束规则,现在总共有22个约束规则,所以它的长度为22。

每一位的值,有三种情况:
-1:代表着绝对规则的true。

0:代表没有设置此条约束。
大于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;
除了约束规则,还有约束方向,也就是说是在哪个方向上面的约束,主要有水平方向和竖直方向。

上面的例子中,他们的约束关系是这样的:
A(Id = 2131165184):
        mRules全是0,mRules[0...21] = 0。
D(Id = 2131165189):
        mRules[1] = mRules[RIGHT_OF] = 2131165184。

        mRules[12] = mRules[ALIGN_PARENT_BOTTOM] = -1。
        其他位都是0。
B(Id = 2131165186):
        mRules[1] = mRules[RIGHT_OF] = 2131165184。
        mRules[2] = mRules[ABOVE] = 2131165189。
        其他位都是0。
C(Id = 2131165187):
        mRules[1] = mRules[RIGHT_OF] = 2131165186。
        其他位都是0。

3、工作过程

RelativeLayout给内部的所有View,分别在水平和竖直两个方向上面,按照一定的规则进行排序(后面会说这个规则),排序后的View,存储在下面这两个数组中:

private View[] mSortedHorizontalChildren;
private View[] mSortedVerticalChildren;

        RelativeLayout会分别在水平和竖直以相同的逻辑去处理这些View,这也就是为什么说在层级相同的情况下,能用LinearLayout就不用RelativeLayout,因为它要做两次处理,分别是水平和竖直方向,会影响性能。

        排序规则,我们知道,ViewGroup在摆放子View的时候,是按照某种顺序一个一个摆放的,那么这就涉及到一个先后顺序的问题。又因为是RelativeLayout内部的子View有相互依赖的关系,所以第一步就需要找出所有的没有相对规则的View,因为这些View不依赖于其他兄弟View,所以可以很明确的知道他们的位置。
        系统把这些没有相对依赖的View称为Roots,下面看一下代码:
/** 根据传进来的规则过滤器,来找到所有的没有相对规则的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;
}


上面的例子中,Roots分别如下:
竖直:
        A、C、D
水平:
        A

找到了Roots,再看看是如何根据这些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");
}
}


上述例子中,这两个数组最后的值为:

mSortedHorizontalChildren:[D、B、C、A]

mSortedVerticalChildren:[A、D、B、C]

有了这两个数组,就可以欢乐的进行 measure、layout 了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: