Android View树的遍历

DecorView

通过PhoneWindow源码可以看到Activity最顶层View是DecorView,状态栏View,底部导航键View,都包含在这里,所以一个Activity里面的所有View组成了一颗有根树,通过遍历这颗有根树,可以得到Activity里的View(除了状态栏&底部导航键栏)
这里主要介绍遍历的方法

递归

首先介绍第一种递归遍历
优点:编写容易,代码量少,不难理解;
缺点:速度慢,深度不知的情况下会导致栈溢出(深度足够大的情况下)

fun traversing_recursive(group: ViewGruup) {
    val count = group.childCount //获取布局子view个数
    for (i in 0 until count){
        val view = group.getChildAt(i)
        if (view is ViewGroup) {
            //子view为布局时
            //自己的处理代码 or 回调出去
            //比如,所有布局背景颜色改成蓝色
            view.setBackgroundColor(Color.BLUE)
            //递归
            traversing_recursive(view)
        }else {
            //子view
            //自己的处理代码 or 回调出去
            //比如,所有TextView字体颜色改成红色
            if (view is TextView) {
                view.setTextColor(Color.RED)
            }
        }
    }
}

循环(有根树先序遍历)

第二种循环遍历
优点:速度快;
缺点:编写较麻烦,理解比递归难点

fun traversing_cycling(group: ViewGroup) {
    var isFinish = false //是否遍历完结
    var tmp = group //当前节点
    tmp.tag = 0 //tag为当前子节点下标
    //检测是否有子节点 or 是否是子节点(防止有个空布局导致遍历结束) & 是否遍历结束
    while ((tmp.childCount > 0 || tmp.parent != null) && !isFinish){
        //是否遍历完所有节点
        if ((tmp.tag as Int) == tmp.childCount){
            //是否是根布局(传进来需要遍历的布局)
            if (tmp == group) {
                isFinish = true //遍历结束
                continue
            }
            //遍历完所有节点则获取父节点
            if (tmp.parent != null)
                tmp = tmp.parent as ViewGroup //继续遍历父节点
            continue
        }
        val index = tmp.tag as Int //获取当前需要遍历的节点下标
        val view = tmp.getChildAt(index) //获取节点
        tmp.tag = index + 1 //节点下标递增
        if (view is ViewGroup) { //是否有子节点
            tmp = view //遍历子节点
            tmp.tag = 0 //初始该节点的遍历子节点下标为0
            //节点->布局,可自行处理,同理递归处理
        }else { //叶节点(所有view都在这里)
            //节点->View,可自行处理,同理递归处理
        }
    }
}

你可能感兴趣的:(Android View树的遍历)