36

getChildAt(i)on 仅获取 a 的直接子级ViewGroup,是否可以在不进行嵌套循环的情况下访问所有子级?

4

8 回答 8

54

在撰写此答案时,已接受的答案存在缺陷,因为它的结果中将包含重复项。

对于那些难以理解递归的人,这里有一个非递归的替代方案。你会因为意识到这也是另一个答案的深度优先方法的广度优先搜索替代方案而获得奖励积分。

private List<View> getAllChildrenBFS(View v) {
    List<View> visited = new ArrayList<View>();
    List<View> unvisited = new ArrayList<View>();
    unvisited.add(v);

    while (!unvisited.isEmpty()) {
        View child = unvisited.remove(0);
        visited.add(child);
        if (!(child instanceof ViewGroup)) continue;
        ViewGroup group = (ViewGroup) child;
        final int childCount = group.getChildCount();
        for (int i=0; i<childCount; i++) unvisited.add(group.getChildAt(i));
    }

    return visited;
}

一些快速测试(不是正式的)表明这种替代方案也更快,尽管这很可能与ArrayList另一个答案创建的新实例的数量有关。此外,结果可能会根据视图层次结构的垂直/水平程度而有所不同。

于 2013-09-07T03:13:22.970 回答
45

来源

如果您想获取所有子视图以及 children 中的视图ViewGroups,则必须递归地执行此操作,因为 API 中没有开箱即用的规定。

private ArrayList<View> getAllChildren(View v) {

    if (!(v instanceof ViewGroup)) {
        ArrayList<View> viewArrayList = new ArrayList<View>();
        viewArrayList.add(v);
        return viewArrayList;
    }

    ArrayList<View> result = new ArrayList<View>();

    ViewGroup viewGroup = (ViewGroup) v;
    for (int i = 0; i < viewGroup.getChildCount(); i++) {

        View child = viewGroup.getChildAt(i);

        ArrayList<View> viewArrayList = new ArrayList<View>();
        viewArrayList.add(v);
        viewArrayList.addAll(getAllChildren(child));

        result.addAll(viewArrayList);
    }
    return result;
}

这将为您提供一个 ArrayList,其中包含层次结构中的所有视图,然后您可以对其进行迭代。

本质上,如果在层次结构中找到另一个 ViewGroup,则此代码会调用自身,然后返回一个 ArrayList 以添加到更大的 ArrayList。

于 2013-09-07T02:01:53.033 回答
3

对于那些使用androidxkotlin

1 - 迭代ViewGroup使用forEach{}

forEach如果要遍历所有子视图,可以在任何 ViewGroup中使用预定义的 kotlin 扩展。例子:

yourViewGroup.forEach{ childView -> // do something with this childView }

.children.toList2 - 返回一个子视图列表ViewGroup

要返回 的列表Views,您可以使用 functionchildren返回 a Sequence,然后使用toList()将其转换为 a List。例子:

val yourChildViewsList: List<View> = yourViewGroup.children.toList()

于 2020-02-04T17:38:03.487 回答
2

我以递归的方式重新实现了这个方法。不确定它是否比 MH 快,但至少它没有重复。

private List<View> getAllViews(View v) {
    if (!(v instanceof ViewGroup) || ((ViewGroup) v).getChildCount() == 0) // It's a leaf
        { List<View> r = new ArrayList<View>(); r.add(v); return r; }
    else {
        List<View> list = new ArrayList<View>(); list.add(v); // If it's an internal node add itself
        int children = ((ViewGroup) v).getChildCount();
        for (int i=0;i<children;++i) {
            list.addAll(getAllViews(((ViewGroup) v).getChildAt(i)));
        }
        return list;
    }
}

MH 的回答包括被赋予该方法的父视图,所以我创建了这个辅助方法(这是要调用的方法)。

我的意思是,如果您在 TextView(没有子视图)上调用此方法,它会返回 0 而不是 1。

private List<View> getAllChildren(View v) {
    List list = getAllViews(v);
    list.remove(v);
    return list;
}

TLDR:要计算所有孩子的复制和粘贴这两种方法,请调用 getAllChildren()

于 2015-02-20T21:18:00.477 回答
1

我不知道它是否好。只需覆盖 onViewAdded(View child) 方法,只需在 .Give it thinks.by List<View>the docs中添加视图。这在您编写自定义视图组时很有用

在将新子项添加到此 ViewGroup 时调用。覆盖应始终调用 super.onViewAdded。

@Override
public void onViewAdded(View child) {
    super.onViewAdded(child);
    views.add(child);//add in list and then use it later

}
于 2016-07-21T11:34:07.570 回答
0

这是我的(递归)解决方案,它像这样打印层次结构:

android.widget.LinearLayout children:2  id:-1
  android.view.View  id:2131624246
  android.widget.RelativeLayout children:2  id:2131624247
    android.support.v7.widget.AppCompatTextView  id:2131624248
    android.support.v7.widget.SwitchCompat  id:2131624249


 public static String getViewHierarcy(ViewGroup v) {
        StringBuffer buf = new StringBuffer();
        printViews(v, buf, 0);
        return buf.toString();
    }

    private static String printViews(ViewGroup v, StringBuffer buf, int level) {
        final int childCount = v.getChildCount();
        v.getId();

        indent(buf, level);
        buf.append(v.getClass().getName());
        buf.append(" children:");
        buf.append(childCount);
        buf.append("  id:"+v.getId());
        buf.append("\n");

        for (int i = 0; i < childCount; i++) {
            View child = v.getChildAt(i);
            if ((child instanceof ViewGroup)) {
                printViews((ViewGroup) child, buf, level+1);
            } else {
                indent(buf, level+1);
                buf.append(child.getClass().getName());
                buf.append("  id:"+child.getId());
                buf.append("\n");
            }
        }
        return buf.toString();
    }

    private static void indent(StringBuffer buf, int level) {
        for (int i = 0; i < level; i++) {
            buf.append("  ");
        }
    }
于 2016-09-13T11:29:49.843 回答
0

我最近遇到了同样的问题,并使用Kotlin 的扩展函数解决了这个问题,如下所示:

fun ViewGroup.getAllChildren(): List<View> {
    val children = ArrayList<View>()
    for (i in 0 until this.childCount) {
        children.add(this.getChildAt(i))
    }
    return children
}
于 2019-11-24T09:31:44.237 回答
0

你可以使用 Kotlin 的扩展函数 LikeViewGroup.children

此方法返回此视图组中子视图的序列。

于 2021-11-09T04:38:04.170 回答