Как захватить содержимое экрана устройства 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-полномочия.