枚举/迭代Activity中的所有视图?

17

有没有一种方法可以遍历所有的Activity中的视图?就像这样:

Iterator it = getViewIterator();
...

这种方法是否存在?


1
我想知道,你为什么想要那个? - Vladimir Ivanov
1
是的,要做这个需要什么要求? - Rohit Sharma
1
请看 http://code.google.com/p/android/issues/detail?id=8488,我的代码在底部附近。 - swinefeaster
2
你可能想要这样做来将自定义字体应用于所有文本字段... 这就是我来到这里的原因 ;) - dy_
我需要做同样的事情,但是在我的应用程序之外,也就是在另一个正在运行的应用程序/ jar 中。这是否可能?如何实现? - AndroidGuy
8个回答

18
如果您所有的视图都在一个LinearLayout或其他扩展了ViewGroup容器中,您可以使用getChildCount()getChildAt(int)函数并遍历所有包含的视图。希望这有所帮助。

是的,这个完美地运作了。谢谢!我不得不在我的基础活动中覆盖我的setContentView()函数,以便我可以存储一个指向根ViewGroup的指针,然后通过你的方法迭代它。 - swinefeaster
1
与其覆盖setContentView,你可以为被膨胀的视图中的最外层容器提供一个ID,并使用findViewById()获取它来代替。 - Austin Hanson

10

假设您所说的“Activity中所有视图”是指整个层次结构中的每个视图,那么在Android中没有任何Iterator或其他允许您迭代所有视图的东西。

ViewGroupgetChildCountgetChildAt方法,但它们仅适用于ViewGroup的直接子项。因此,您需要访问每个子项,并检查它是否是一个ViewGroup本身,以此类推。长话短说,我想要一种查找与特定标记匹配的所有视图的功能(findViewWithTag仅返回第一个)。下面是一个可以迭代所有视图的类:

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

使用方法如下:

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

1
这个类的源代码作者页面链接:http://android-wtf.com/2013/06/how-to-easily-traverse-any-view-hierarchy-in-android/ - ruX

2

以下是我根据Harry Joy的答案编写的示例。它遍历父ViewGroup的所有层次结构,以查看其中是否有任何视图是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;

    }

1

Activity通常包含一个主要的布局容器,其中放置了所有其他视图。 使用主布局容器的引用。遍历其子项(使用getChild(position),getchildcount()等),如果任何子项本身是容器,则在其上应用相同的函数。这有点像遍历树形结构。


1

一个小的递归函数,用于枚举所有视图

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

使用 Kotlin 编写。

要使用此函数:

processAllViews( [~rootLayoutId~] )

或者如果您没有/不想要根布局 ID

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

0

RxJava 解决方案

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

0

如果你真的需要一个迭代器,API 中没有类似的东西可用。你将不得不编写自己的迭代器,建立在 Harry Joy 和 Javanator 建议的逻辑之上。


0

如果你想避免丑陋的 for (int i = 0; ....), 你可以使用一个静态包装器

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

        };
    }
}

然后像这样使用它

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

当然,这样做效率略低,因为每次迭代都会创建一个新的可迭代对象,但如果您愿意,可以在此基础上构建更有效的算法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接