都知道RelativeLayout的一次测量调用两次子视图测量循环
横向一次 纵向一次
带着目的, 我们来分析源码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mDirtyHierarchy) {
mDirtyHierarchy = false;
sortChildren();
}
一上来就是重要代码!
如果布局层次是脏的(无效、或过时失效) 那么sortChildren!!!! 这个方法是简历Relative布局关系的核心
private void sortChildren() {
final int count = getChildCount();
//懒加载初始化两个View数组 分别存放横向 纵向
if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
mSortedVerticalChildren = new View[count];
}
if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
mSortedHorizontalChildren = new View[count];
}
final DependencyGraph graph = mGraph; //描述关系的graph
graph.clear();
//初始 清空graph
for (int i = 0; i < count; i++) {
graph.add(getChildAt(i)); //先都add收集
}
graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL); //再把两个方向分别sort
graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
}
CoordinatorLayout里的DAG这里没有使用, 而是用了一个内部类DependencyGraph描述graph
private static class DependencyGraph {
mNodes存放所有一级子View
/**
* List of all views in the graph.
*/
private ArrayList<Node> mNodes = new ArrayList<Node>();
mKeyNodes存放有id的一级子View
/**
* List of nodes in the graph. Each node is identified by its
* view id (see View#getId()).
*/
private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
mRoots临时数据结构,用于有序的放置目标的View数组,注意:这里的数据结构是ArrayDeque 双端队列 为什么要用这个数据结构 在getSortedViews里会讲到
/**
* Temporary data structure used to build the list of roots
* for this graph.
*/
private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
循环收集“”一级”子View的方法 add(注意是一级,二级里加dependency xml也会给你报错!)
/**
* Adds a view to the graph. 把view都收集在graph里
*
* @param view The view to be added as a node to the graph.
*/
void add(View view) {
final int id = view.getId();
final Node node = Node.acquire(view); //内部类Node描述节点
if (id != View.NO_ID) {
mKeyNodes.put(id, node); //所有有id的View都会被存放在mKeyNodes里,因为有可能被依赖
}
mNodes.add(node); //所有的子View都收集在mNodes里
}
核心方法getSortedViews排序后放入View数组里
/**
* Builds a sorted list of views. The sorting order depends on the dependencies
* between the view. For instance, if view C needs view A to be processed first
* and view A needs view B to be processed first, the dependency graph
* is: B -> A -> C. The sorted array will contain views B, A and C in this order.
* 创建有序的view数组。“序”取决于View之间的dependencies关系。
* 例如,如果viewC的布局需要先viewA先处理,而viewA的布局又需要先viewB先处理
* 那么依赖图就是:B -> A -> C 有序数组将这么排序:{viewB,viewA, viewC}。
*
* @param sorted The sorted list of views. The length of this array must
* be equal to getChildCount().
* @param rules The list of rules to take into account.
*/
void getSortedViews(View[] sorted, int... rules) {
final ArrayDeque<Node> roots = findRoots(rules); //找出所有的没有依赖的node 就是root
int index = 0;
Node node;
// 啥是pollLast: 获取并移除此列表尾部的最后一个元素
while ((node = roots.pollLast()) != null) { //把roots根节点依次pollLast出来
final View view = node.view;
final int key = view.getId();
sorted[index++] = view; //然后设置到数组的对应位置
//是不是搞错了 为什么只加了根节点?别急
// 在这个方法内部 所有的根节点rootA被设置完之后,
//实际上会把(被rootA直接依赖的)rootB加在roots尾部,进行下一次while循环...
//直到这个rootN没有被依赖的root了,才会while到下一个findRoots方法出来的无依赖root
final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
final int count = dependents.size(); //找到这个node的被依赖树graph
for (int i = 0; i < count; i++) {
final Node dependent = dependents.keyAt(i);
final SparseArray<Node> dependencies = dependent.dependencies;
dependencies.remove(key); //优化,移除掉处理过的root
if (dependencies.size() == 0) { //把被依赖树的顶层(也就是当前root的直接被依赖)
//作为下一次while循环的root对象 add到roots的尾部
roots.add(dependent);
}
}
}
if (index < sorted.length) { //如果数组没设置满就跳出了while循环,
//说明有循环依赖! 源码是会抛出异常的!!&