简体   繁体   中英

Android Screen Capture using root view

I am trying to capture the screen

    View decorView = activity.getWindow().getDecorView();
    decorView.setDrawingCacheEnabled(true);
    Bitmap bitmap = decorView.getDrawingCache();

Everything is working fine, I am able to take the screenshots. But for one scenario I am facing one issue. But when there is Dialog fragment, the view of Dialog fragment is not getting captured, but the view of background activity which is partial visible is getting captured as if the root view of activity in the topmost layer.

But when there is Dialog fragment, the view of Dialog fragment is not getting captured, but the view of background activity which is partial visible is getting captured as if the root view of activity in the topmost layer.

That is because the Dialog is in a separate window. I am not aware of anything much that you can do about this.

You can get list of decor views for all windows of your app with the help of reflection. This list will contain decor views not only for activities, but also for dialogs and popup windows.

void processDialogWindow(Context context) {
    WindowManager windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    Field globalField = Class.forName("android.view.WindowManagerImpl").getDeclaredField("mGlobal");
    globalField.setAccessible(true);
    Object globalFieldValue = globalField.get(windowManager);
    List<?> viewsFieldValue = getViews(globalFieldValue);
    for (int i = viewsFieldValue.size() - 1; i >= 0; i--) {
        View decorView = (View)viewsFieldValue.get(i);
        WindowManager.LayoutParams castedParams = (WindowManager.LayoutParams)decorView.getLayoutParams();
        if (castedParams.type == WindowManager.LayoutParams.TYPE_APPLICATION) { // Dialog
            //TODO: get decorView screenshot.
        }
    }
}

List<?> getViews(Object globalWindowManager) throws NoSuchFieldException, IllegalAccessException {
    Field viewsField = globalWindowManager.getClass().getDeclaredField("mViews");
    viewsField.setAccessible(true);
    Object viewsFieldValue = viewsField.get(globalWindowManager);
    if (viewsFieldValue instanceof List<?>)
        return (List<?>)viewsFieldValue;
    else if (viewsFieldValue instanceof View[]) {
        return new ArrayList<>(Arrays.asList((View[])viewsFieldValue));
    }
    return null;
}

I have tested this approach on Android 5, 6 and 7. Certainly, when you use reflection, you should check if the value of some field is not null and really has expected type. Thus internal data structures, which are accessed via reflection, can be changed in future versions of Android or by device vendor even in existing Android versions. I didn't add checks for null or wrong type here for the sake of conciseness.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM