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

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

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

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

8 ответов

Решение

По этой ссылке можно использовать ddms в каталоге инструментов android sdk, чтобы делать снимки экрана.

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

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

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

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

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

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

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

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

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

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

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

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

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.)

Вот фрагмент кода для 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;
}

Вы можете попробовать следующую библиотеку: Библиотека скриншотов Android (ASL) позволяет программно захватывать скриншоты с устройств Android без необходимости иметь права root-доступа. Вместо этого ASL использует собственный сервис, работающий в фоновом режиме, запускаемый через Android Debug Bridge (ADB) один раз за загрузку устройства.

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

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

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

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

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

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

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

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

Другие вопросы по тегам