Как захватить содержимое экрана устройства Android?

Возможный дубликат:
Как программно сделать снимок экрана на Android?

Как захватить содержимое экрана устройства Android и сделать файл изображения с использованием данных моментальных снимков? Какой API я должен использовать или где я могу найти связанные ресурсы?

BTW: не снимок камеры, но экран устройства

Ответ 1

В соответствии с эта ссылка, можно использовать ddms в каталоге tools android sdk для захвата экрана.

Для этого в приложении (а не во время разработки) есть приложения для этого. Но, как указывает @zed_0xff, он, безусловно, требует root.

Ответ 2

Используйте следующий код:

Bitmap bitmap;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);

Здесь MyView - это View, через которое нам нужно включить в экран. Вы также можете получить DrawingCache из любого View таким образом (без getRootView()).


Есть и другой способ.
Если у нас есть ScrollView как корневой вид, то лучше использовать следующий код,

LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
FrameLayout root = (FrameLayout) inflater.inflate(R.layout.activity_main, null); // activity_main is UI(xml) file we used in our Activity class. FrameLayout is root view of my UI(xml) file.
root.setDrawingCacheEnabled(true);
Bitmap bitmap = getBitmapFromView(this.getWindow().findViewById(R.id.frameLayout)); // here give id of our root layout (here its my FrameLayout id)
root.setDrawingCacheEnabled(false);

Вот метод getBitmapFromView()

public static Bitmap getBitmapFromView(View view) {
        //Define a bitmap with the same size as the view
        Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
        //Bind a canvas to it
        Canvas canvas = new Canvas(returnedBitmap);
        //Get the view background
        Drawable bgDrawable =view.getBackground();
        if (bgDrawable!=null) 
            //has background drawable, then draw it on the canvas
            bgDrawable.draw(canvas);
        else 
            //does not have background drawable, then draw white background on the canvas
            canvas.drawColor(Color.WHITE);
        // draw the view on the canvas
        view.draw(canvas);
        //return the bitmap
        return returnedBitmap;
    }

Он отобразит весь экран, включая контент, скрытый в вашем ScrollView


ОБНОВЛЕНО ОТ 20-04-2016

Существует еще один лучший способ сделать снимок экрана.
Здесь я сделал снимок экрана WebView.

WebView w = new WebView(this);
    w.setWebViewClient(new WebViewClient()
    {
        public void onPageFinished(final WebView webView, String url) {

            new Handler().postDelayed(new Runnable(){
                @Override
                public void run() {
                    webView.measure(View.MeasureSpec.makeMeasureSpec(
                                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED),
                            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                    webView.layout(0, 0, webView.getMeasuredWidth(),
                            webView.getMeasuredHeight());
                    webView.setDrawingCacheEnabled(true);
                    webView.buildDrawingCache();
                    Bitmap bitmap = Bitmap.createBitmap(webView.getMeasuredWidth(),
                            webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

                    Canvas canvas = new Canvas(bitmap);
                    Paint paint = new Paint();
                    int height = bitmap.getHeight();
                    canvas.drawBitmap(bitmap, 0, height, paint);
                    webView.draw(canvas);

                    if (bitmap != null) {
                        try {
                            String filePath = Environment.getExternalStorageDirectory()
                                    .toString();
                            OutputStream out = null;
                            File file = new File(filePath, "/webviewScreenShot.png");
                            out = new FileOutputStream(file);

                            bitmap.compress(Bitmap.CompressFormat.PNG, 50, out);
                            out.flush();
                            out.close();
                            bitmap.recycle();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, 1000);
        }
    });

Надеюсь, это поможет..!

Ответ 3

AFAIK. Все методы, используемые для захвата скриншота Android, используют фреймбуфер /dev/graphics/fb 0. Это включает в себя ddms. Это требует, чтобы корень читался из этого потока. ddms использует adbd для запроса информации, поэтому root не требуется, поскольку adb имеет разрешения, необходимые для запроса данных из /dev/graphics/fb 0.

Фреймбуфер содержит 2 + "кадры" изображений RGB565. Если вы можете прочитать данные, вам нужно знать разрешение экрана, чтобы узнать, сколько байтов необходимо для получения изображения. каждый пиксель составляет 2 байта, поэтому, если разрешение экрана было 480x800, вам нужно было бы прочитать 768 000 байт для изображения, так как изображение 480x800 RGB565 имеет 384 000 пикселей.

Ответ 4

Для новых платформ Android можно выполнить системную утилиту screencap в /system/bin, чтобы получить скриншот без разрешения root. Вы можете попробовать "/system/bin/screencap -h", чтобы узнать, как использовать его в adb или любой оболочке.

Кстати, я думаю, что этот метод хорош только для одного моментального снимка. Если мы хотим захватить несколько кадров для воспроизведения экрана, это будет слишком медленно. Я не знаю, существует ли какой-либо другой подход для более быстрого захвата экрана.

Ответ 5

[На основе исходного кода Android:]

На стороне С++ SurfaceFlinger реализует API captureScreen. Это отображается через интерфейс IPC связующего, каждый раз возвращая новую область золы, которая содержит необработанные пиксели с экрана. Фактический снимок экрана выполняется через OpenGL.

Для системных клиентов С++ интерфейс отображается через класс ScreenshotClient, определенный в <surfaceflinger_client/SurfaceComposerClient.h> для Android < 4,1; для Android > 4.1 использовать <gui/SurfaceComposerClient.h>

До JB, чтобы сделать снимок экрана в программе на С++, этого было достаточно:

ScreenshotClient ssc;
ssc.update();

С JB и несколькими дисплеями он становится немного сложнее:

ssc.update(
    android::SurfaceComposerClient::getBuiltInDisplay(
        android::ISurfaceComposer::eDisplayIdMain));

Затем вы можете получить к нему доступ:

do_something_with_raw_bits(ssc.getPixels(), ssc.getSize(), ...);

Используя исходный код Android, вы можете скомпилировать свою собственную общую библиотеку для доступа к этому API, а затем выставить ее через JNI на Java. Чтобы создать снимок экрана из вашего приложения, приложение должно иметь разрешение READ_FRAME_BUFFER. Но даже тогда, по-видимому, вы можете создавать снимки экрана только из системных приложений, то есть тех, которые подписаны с тем же ключом, что и система. (Эта часть я до сих пор не совсем понимаю, так как я недостаточно хорошо знаком с системой Android Permissions.)

Вот фрагмент кода для JB 4.1/4.2:

#include <utils/RefBase.h>
#include <binder/IBinder.h>
#include <binder/MemoryHeapBase.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>

static void do_save(const char *filename, const void *buf, size_t size) {
    int out = open(filename, O_RDWR|O_CREAT, 0666);
    int len = write(out, buf, size);
    printf("Wrote %d bytes to out.\n", len);
    close(out);
}

int main(int ac, char **av) {
    android::ScreenshotClient ssc;
    const void *pixels;
    size_t size;
    int buffer_index;

    if(ssc.update(
        android::SurfaceComposerClient::getBuiltInDisplay(
            android::ISurfaceComposer::eDisplayIdMain)) != NO_ERROR ){
        printf("Captured: w=%d, h=%d, format=%d\n");
        ssc.getWidth(), ssc.getHeight(), ssc.getFormat());
        size = ssc.getSize();
        do_save(av[1], pixels, size);
    }
    else
        printf(" screen shot client Captured Failed");
    return 0;
}

Ответ 6

Вы можете попробовать следующую библиотеку: http://code.google.com/p/android-screenshot-library/ Android Screenshot Library (ASL) позволяет программно снимать скриншоты с устройств Android без необходимости иметь права доступа root. Вместо этого ASL использует собственный сервис, работающий в фоновом режиме, который запускается через Android Debug Bridge (ADB) один раз за загрузку устройства.

Ответ 7

если вы хотите сделать захват экрана из кода Java в приложении Android AFAIK, у вас должны быть корневые резервные копии.

Ответ 8

Framebuffer, похоже, подходит, он не всегда будет содержать 2+ фрейма, как упоминалось Райаном Конрадом. В моем случае он содержал только один. Я думаю, это зависит от размера рамки/дисплея.

Я пытался непрерывно читать фреймбуфер, но, похоже, он возвращается для фиксированного количества прочитанных байтов. В моем случае это (3 410 432) байт, что достаточно для хранения кадра отображения 854 * 480 RGBA (3 279 360 байт). Да, кадр в двоичном формате, выводимый из fb0, - это RGBA на моем устройстве. Это скорее всего будет зависеть от устройства к устройству. Это будет важно для вас, чтобы расшифровать его =)

В моих разрешениях device/dev/graphics/fb0 так, что только root, а пользователи из групповой графики могут читать fb0. графика - это ограниченная группа, поэтому вы, вероятно, будете иметь доступ только к fb0 с помощью корневого телефона с помощью команды su.

Android-приложения имеют идентификатор пользователя (uid) app _ ## и группу id (guid) app _ ##.

adb shell имеет uid shell и оболочку guid, которая имеет гораздо больше разрешений, чем приложение. Фактически вы можете проверить эти разрешения на уровне /system/permissions/platform.xml

Это означает, что вы сможете читать fb0 в оболочке adb без root, но вы не будете читать его в приложении без root.

Кроме того, предоставление разрешений READ_FRAME_BUFFER и/или ACCESS_SURFACE_FLINGER для AndroidManifest.xml ничего не сделает для обычного приложения, потому что они будут работать только для приложений подписи.