Android | Получить все дочерние элементы группы ViewGroup

getChildAt(i) on получает только прямые дочерние элементы ViewGroup, возможно ли получить доступ ко всем дочерним элементам без выполнения вложенных циклов?

Ответ 1

(источник)

Если вы хотите получить все дочерние представления, а также представления в дочерних элементах 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.

Ответ 2

Во время написания этого ответа принятый ответ ошибочен тем, что он будет содержать дубликаты в своем результате.

Для тех, у кого есть проблемы, обертывание головы вокруг рекурсии, здесь нерекурсивная альтернатива. Вы получаете бонусные баллы за то, что понимаете, что это также альтернативный вариант поиска в глубину первого варианта ответа.

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, которые создает другой ответ. Кроме того, результаты могут варьироваться в зависимости от вертикальной/горизонтальной иерархии представлений.

Ответ 3

Я повторил этот метод рекурсивным образом. Не уверен, что это быстрее, чем MH, но по крайней мере у него нет дубликатов.

private List<View> getAllViews(View v) {
    if (!(v instanceof ViewGroup) || ((ViewGroup) v).getChildCount() == 0) // It 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 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;
}

TL;DR: чтобы подсчитать всех детей, скопируйте и вставьте оба метода, вызовите getAllChildren()

Ответ 4

Я не знаю, хорошо ли это или нет. Просто переопределите метод onViewAdded (View child) и просто добавьте представления в List<View>. Выделите его мышью. docs. Это полезно, когда вы пишете пользовательскую группу просмотра

Вызывается, когда к этой группе ViewGroup добавляется новый ребенок. Переопределения должны всегда вызывать super.onViewAdded.

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

}

Ответ 5

Вот мое (рекурсивное) решение, которое печатает иерархию следующим образом:

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("  ");
        }
    }