Home » Android » Android | Get all children elements of a ViewGroup

Android | Get all children elements of a ViewGroup

Posted by: admin April 23, 2020 Leave a comment

Questions:

getChildAt(i) on gets only the direct children of a ViewGroup, is it possible to access to all children without doing nested loops?

How to&Answers:

(source)

If you want to get all the child views, as well as the views within children ViewGroups, you must do it recursively, since there is no provision in the API to do this out of the box.

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;
}

This will give you an ArrayList with all the Views in the hierarchy which you can then iterate over.

Essentially, this code call itself if it finds another ViewGroup in the hierarchy, and then returns an ArrayList to be added to the bigger ArrayList.

Answer:

At the time of writing this answer, the accepted answer is flawed in that it will contains duplicates in its result.

For those who have trouble wrapping their head around recursion, here’s a non-recursive alternative. You get bonus points for realizing this is also a breadth-first search alternative to the depth-first approach of the other answer.

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;
}

A couple of quick tests (nothing formal) suggest this alternative is also faster, although that has most likely to do with the number of new ArrayList instances the other answer creates. Also, results may vary based on how vertical/horizontal the view hierarchy is.

Answer:

I’ve reimplemented this method in a recursive way. Not sure it’s faster than MH’s, but at least it has no duplicates.

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’s answer includes the parent view which is being given to the method, so I created this auxiliary method (which is the one to be called).

I mean, if you call this method on a TextView (which has no children), it returns 0 instead of 1.

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

TLDR: to count all children copy and paste both methods, call getAllChildren()

Answer:

I don’t know if its good or not.just override the onViewAdded(View child) method and just add the views in List<View>.Give it thoughts.by the docs.This usefull when you are writing custom view group

Called when a new child is added to this ViewGroup. Overrides should always call super.onViewAdded.

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

}

Answer:

Here is my (recursive) solution that prints the hierarcy like this:

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

Answer:

I recently had the same issue and solved it using Kotlin’s extension functions like this:

fun ViewGroup.getAllChildren(): List<View> {
    val children = ArrayList<View>()
    for (i in 0 until this.childCount) {
        children.add(this.getChildAt(i))
    }
    return children
}

Answer:

For those using androidx and kotlin

1 – Iterate through ViewGroup using forEach{}

If you want to iterate through all childViews, you can use predefined kotlin extension forEach in any ViewGroup. Example:

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

2 – Return a list of childViews using .children.toList in a ViewGroup

To return a list of Views, you can use function children to return a Sequence and then use toList() to transform it to a List. Example:

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