22

是否可以以编程方式枚举android.view.Window应用程序中的所有 s 或装饰视图?

Dialogs例如,两者都将在新的 Activity 窗口中打开Window,与主 Activity 窗口分开。我可以通过以下方式找到它们,Dialog.getWindow()但我不确定如何使用内置组件(例如活动菜单弹出窗口)来做到这一点。

有什么方法可以从Application, Context, 或WindowManager, 或其他东西中枚举与我的应用程序关联的 Windows 吗?

我可以看到我的所有应用程序的窗口adb dumpsys window,但我正在寻找一种方法来在我的应用程序中执行此操作而无需 root。

4

6 回答 6

23

我找到了一种通过对@hidden WindowManagerGlobal. 至少到目前为止,我知道这适用于 android-18。

private void logRootViews() {
    try {
        Class wmgClass = Class.forName("android.view.WindowManagerGlobal");                        
        Object wmgInstnace = wmgClass.getMethod("getInstance").invoke(null, (Object[])null);

        Method getViewRootNames = wmgClass.getMethod("getViewRootNames"); 
        Method getRootView = wmgClass.getMethod("getRootView", String.class);
        String[] rootViewNames = (String[])getViewRootNames.invoke(wmgInstnace, (Object[])null);

        for(String viewName : rootViewNames) {
            View rootView = (View)getRootView.invoke(wmgInstnace, viewName);
            Log.i(TAG, "Found root view: " + viewName + ": " + rootView);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

输出:

找到根视图:com.example.paintsample/com.example.paintsample.PaintSample/android.view.ViewRootImpl@41deeff0: com.android.internal.policy.impl.PhoneWindow$DecorView{41dcc278 VE.... R... .... 0,0-768,1184}

找到根视图:PopupWindow:42887380/android.view.ViewRootImpl@42891820: android.widget.PopupWindow$PopupViewContainer{42891450 VE.... ........ 0,0-424,618}

当然,对于任何能找到更好方法的人来说,赏金仍然可以争夺:)

于 2013-11-04T20:25:58.720 回答
15

我不完全确定这回答了实际问题,但它是一种更好的方式来获取所有根视图,如已接受的答案中所建议的那样。

如那里所述,我还设法仅使用反射来完成此操作,除了此代码支持API 14及更高版本的所有版本(我没有在下面检查):

public static List<View> getWindowManagerViews() {
    try {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
                Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {

            // get the list from WindowManagerImpl.mViews
            Class wmiClass = Class.forName("android.view.WindowManagerImpl");
            Object wmiInstance = wmiClass.getMethod("getDefault").invoke(null);

            return viewsFromWM(wmiClass, wmiInstance);

        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {

            // get the list from WindowManagerGlobal.mViews
            Class wmgClass = Class.forName("android.view.WindowManagerGlobal");
            Object wmgInstance = wmgClass.getMethod("getInstance").invoke(null);

            return viewsFromWM(wmgClass, wmgInstance);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return new ArrayList<View>();
}

private static List<View> viewsFromWM(Class wmClass, Object wmInstance) throws Exception {

    Field viewsField = wmClass.getDeclaredField("mViews");
    viewsField.setAccessible(true);
    Object views = viewsField.get(wmInstance);

    if (views instanceof List) {
        return (List<View>) viewsField.get(wmInstance);
    } else if (views instanceof View[]) {
        return Arrays.asList((View[])viewsField.get(wmInstance));
    }

    return new ArrayList<View>();
}
于 2016-12-12T14:27:46.553 回答
4

SDK 附带的Hierarchyviewer工具物超所值。

于 2013-11-04T20:34:09.637 回答
2

您可以直接使用@hidden API,而无需通过访问类文件并将其添加到Android SDK 中的android.jar 来使用反射。方法如下: https ://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/

并且可以在这里获取特定android版本(19、21、22、23、24)的android.jar的来源: https ://github.com/anggrayudi/android-hidden-api

因此,您可以直接使用 WindowManagerGlobal 类来获取所有根视图,例如,

private void logRootViews() {
    WindowManagerGlobal windowManagerGlobal = WindowManagerGlobal.getInstance();
    String[] rootViewNames = windowManagerGlobal.getViewRootNames();

    for (String viewName : rootViewNames) {
        View rootView = windowManagerGlobal.getRootView(viewName);
        Log.i("", "Root view is: " + viewName + ": " + rootView);
        /*do what you want with the rootView*/
    }
}

输出:

根视图是:com.example.paintsample/com.example.paintsample.PaintSample/android.view.ViewRootImpl@41deeff0: com.android.internal.policy.impl.PhoneWindow$DecorView{41dcc278 VE.... R... .... 0,0-768,1184}

根视图是:PopupWindow:42887380/android.view.ViewRootImpl@42891820: android.widget.PopupWindow$PopupViewContainer{42891450 VE.... ........ 0,0-424,618}

于 2017-03-10T13:07:08.630 回答
2

那些负担得起的人minSdk 29可以使用WindowInspector.getGlobalWindowViews()。在内部,它指的是mViews来自 的属性WindowManagerGlobal,但可供公众使用。

于 2021-03-18T09:55:33.387 回答
0

每个解决方案都在上面的Java中,我为Kotlin制作了转换Andrew Lavers 答案的解决方案-

try
    {
        val wmgClass = Class.forName("android.view.WindowManagerGlobal")
        val wagInstance = wmgClass.getMethod("getInstance").invoke(null)
        val getViewRootNames: Method = wmgClass.getMethod("getViewRootNames")
        val getRootView: Method = wmgClass.getMethod("getRootView", String::class.java)
        val rootViewNames = getViewRootNames.invoke(wagInstance) as Array<String>
        for (viewName in rootViewNames) {
            val rootView = getRootView.invoke(wagInstance, viewName) as View

        }
    } catch (exception: java.lang.Exception {}
于 2020-09-22T09:43:40.987 回答