如何找到微调器显示的下拉视图

How to find a spinner's displayed drop down views

我公司的应用程序支持在运行时动态更改主题,而无需重新启动当前 Activity。它通过遍历视图树并将样式应用于配置为支持它的每个视图来实现这一点。然而,我 运行 遇到的问题是,在遍历视图树时,从未找到为微调器显示的下拉视图。这是从 Activity:

中查找每个视图的代码
void applyTheme(final int resourceId) {
    setTheme(resourceId);

    // Apply the theme to the activity's view.
    styleView(findViewById(android.R.id.content));

    // Apply the theme to any dialog fragments.
    final List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (final Fragment fragment : fragments) {
        if (fragment instanceof DialogFragment) {
            final Dialog dialog = ((DialogFragment)fragment).getDialog();
            if (dialog != null) {
                final Window window = dialog.getWindow();
                if (window != null) {
                    styleView(window.getDecorView());
                }
            }
        }
    }
}

void styleView(final View view) {
    // Apply styling here.
    ...

    // Recursively find all children and apply the theme to them.
    if (view instanceof ViewGroup) {
        final ViewGroup viewGroup = (ViewGroup)view;
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            styleView(viewGroup.getChildAt(i));
        }
    }
}

无论父 Spinner 是在 Activity、Fragment 还是 DialogFragment 中定义,都找不到下拉视图。如果下拉视图对用户可见,是否有任何方法可以实际检索对它们的引用?

This answer 为我提供了一种查找所有视图的方法,包括 PopupWindows 的视图。不幸的是,它利用了反射,并不能保证在所有 API 版本上工作,但它似乎在我们的应用程序支持的 APIs 上工作(我在 APIs 19 上测试和 28).这是更改 applyTheme():

的结果
private Object wmgInstance = null;
private Method getViewRootNames = null;
private Method getRootView = null;

private void applyTheme(final int resourceId)
{
    setTheme(resourceId);

    try
    {
        // Find all possible root views (the Activity, any PopupWindows, and Dialogs). This logic should work with APIs 17-28.
        if (wmgInstance == null)
        {
            final Class wmgClass = Class.forName("android.view.WindowManagerGlobal");
            wmgInstance = wmgClass.getMethod("getInstance").invoke(null, (Object[])null);

            getViewRootNames = wmgClass.getMethod("getViewRootNames");
            getRootView = wmgClass.getMethod("getRootView", String.class);
        }

        final String[] rootViewNames = (String[])getViewRootNames.invoke(wmgInstance, (Object[])null);

        for (final String viewName : rootViewNames)
        {
            final View rootView = (View)getRootView.invoke(wmgInstance, viewName);
            styleView(rootView);
        }
    }
    catch (Exception e)
    {
        // Log the exception.
    }
}

这个逻辑成功地允许我更新 Spinner 的下拉视图,当它们在主题更改期间可见时。但是,仍然存在一个问题,如果 Spinner 的下拉视图是在主题更改发生之前创建的,但下拉列表在主题更改时关闭,然后重新打开 Spinner 会显示使用旧主题的视图。我处理这个问题的方法只是在我的适配器 getDropDownView().

的视图中调用 styleView()