开发者

Enumerate/Iterate all Views in Activity?

Is there a way to iterate through 开发者_Python百科all the views in your Activity? Something like:

Iterator it = getViewIterator();
...

Does this exist at all?


If you have all your Views in a LinearLayout or an other container that extends ViewGroup you can use the functions getChildCount() and getChildAt(int) and iterate through all of the
contained views.

Hope this helps.


Assuming by "all the views in your Activity" you mean every view in the hierarchy, there's no such Iterator or anything in Android that allows you iterate over all views.

ViewGroups have getChildCount and getChildAt methods, but they are about direct children of the ViewGroup. So, you would need to visit every child, and check if it's a ViewGroup itself, and so on. Long story short, I wanted such a functionality to find all views that match a specific tag (findViewWithTag only returns the first). Here is a class which can iterate all views:

public class LayoutTraverser {

    private final Processor processor;


    public interface Processor {
        void process(View view);
    }

    private LayoutTraverser(Processor processor) {
        this.processor = processor;
    }

    public static LayoutTraverser build(Processor processor) {
        return new LayoutTraverser(processor);
    }

    public View traverse(ViewGroup root) {
        final int childCount = root.getChildCount();

        for (int i = 0; i < childCount; ++i) {
            final View child = root.getChildAt(i);

            if (child instanceof ViewGroup) {
                traverse((ViewGroup) child);
            }
        }
        return null;
    }
}

Use it like this:

LayoutTraverser.build(new LayoutTraverser.Processor() {
  @Override
  public void process(View view) {
    // do stuff with the view
  }
}).traverse(viewGroup);


Here is my example, based on Harry Joy's answer. It iterates through all hierarchies of the parent ViewGroup to see if any of the views anywhere within it are a Button:

private boolean doesViewGroupContainButton(ViewGroup parentView) {

        int numChildViews = parentView.getChildCount();
        for (int i = 0; i < numChildViews; i++) {
            View childView = parentView.getChildAt(i);
            if (childView instanceof ViewGroup) {
                if (doesViewContainButton((ViewGroup)childView)) {
                    return true;
                }
            }
            else if (childView instanceof Button) {
                return true;
            }
        }
        return false;

    }


Activity Generally contains one main Layout Container in which all other views are placed. Using the reference of Main Layout Container. Traverse through its child (Use getChild(postion) , getchildcount() etc). and if any child is a container itself then apply the same function on it..This is some like traversing a tree structure


If you actually need an iterator, there isn't anything like that available in the api. You'll have to write your own that is built on top of the logic that Harry Joy and Javanator suggest.


If you want to avoid the ugly for (int i = 0; ....) you can use a static wrapper

public class Tools
{
    public static Iterable<View> getChildViews(final ViewGroup view)
    {
        return new Iterable<View>()
        {
            int i = -1;

            @NonNull
            @Override
            public Iterator<View> iterator()
            {
                return new Iterator<View>()
                {
                    int i = -1;
                    @Override
                    public boolean hasNext()
                    {
                        return i < view.getChildCount();
                    }

                    @Override
                    public View next()
                    {
                        return view.getChildAt(++i);
                    }
                };
            }

        };
    }
}

and use it like so

for (View view :  Tools.getChildViews(viewGroup))

of course this is slightly less efficient since you are creating a new Iteratable object every time you iterate, but you could build on this a more efficient algo if you want.


Rx java solution

    public static Observable<View> iterate(@NonNull View root) {
        return Observable.create(emitter -> {
            iterate(root, emitter);
            emitter.onComplete();
        });
    }

    private static void iterate(@NonNull View view, @NonNull ObservableEmitter<View> emitter) {
        emitter.onNext(view);
        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                iterate(viewGroup.getChildAt(i), emitter);
            }
        }
    }


Small recursive function which does enumerating all views

 private fun processAllViews(viewGroup: ViewGroup) {
    for (child in viewGroup.children) {
        if (child is EditText) {
           // do some stuff with EditText
        } else if (child !is ViewGroup) {
            // do some stuff with not EditText and not ViewGroup
        } else {
            processAllViews(child as ViewGroup) // process inner layout
        }
    }
}

Written in Kotlin.

To use this function:

processAllViews( [~rootLayoutId~] )

or if you don't have / don't want root layout id

val baseContent = (findViewById<View>(android.R.id.content) as ViewGroup).getChildAt(0) as ViewGroup
processAllViews(baseContent)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜