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 了。
相关文章推荐
- 【转】图解 HTTP协议/IIS 原理及ASP.NET运行机制浅析
- while(cin>>val)的原理浅析
- Android框架浅析之锁屏(Keyguard)机制原理 .
- Android框架浅析之锁屏(Keyguard)机制原理
- Android框架浅析之锁屏(Keyguard)机制原理
- 热门Web开发方式 REST实现原理浅析
- 相见恨晚之gstreamer 核心源码source创建和识别typefind原理浅析(一)
- 浅析前端路由简介以及vue-router实现原理
- CLR 中匿名函数的实现原理浅析
- 内网穿透-ngrok原理浅析
- Windows NT Session 概念的原理与应用浅析 [1] 遍历并获取信息 [草稿]
- ASP.NET 中 Session 实现原理浅析 [2] 状态管理器
- 硬件路由转发原理浅析
- Netfilter的原理和实现浅析
- 网易邮箱添加附件功能原理浅析
- RT-Thread下finsh原理浅析
- 浅析SSH核心原理(二)
- 浅析 Linux 中的时间编程和实现原理,第 2 部分: 硬件和 GLibC 库的细节
- hashmap实现原理浅析