如何捕获android设备屏幕内容?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3067586/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to capture the android device screen content?
提问by Jagie
Possible Duplicate:
How to programatically take a screenshot on Android?
How to capture the android device screen content and make an image file using the snapshot data? Which API should I use or where could I find related resources?
如何捕获android设备屏幕内容并使用快照数据制作图像文件?我应该使用哪个 API 或在哪里可以找到相关资源?
BTW: not camera snapshot, but device screen
顺便说一句:不是相机快照,而是设备屏幕
采纳答案by Joubarc
According to this link, it is possible to use ddms in the tools directory of the android sdk to take screen captures.
根据这个链接,可以使用android sdk的tools目录下的ddms来截屏。
To do this within an application (and not during development), there are also applications to do so. But as @zed_0xff points out it certainly requires root.
要在应用程序中(而不是在开发期间)执行此操作,也有应用程序可以执行此操作。但正如@zed_0xff 指出的那样,它肯定需要 root。
回答by Nirav Dangi
Use the following code:
使用以下代码:
Bitmap bitmap;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
Here MyView
is the View
through which we need include in the screen. You can also get DrawingCache
from of any View
this way (without getRootView()
).
这MyView
是View
我们需要包含在屏幕中的方法。您也可以DrawingCache
从任何View
这种方式(没有getRootView()
)中获得。
There is also another way..
If we having ScrollView
as root view then its better to use following code,
还有另一种方法..
如果我们有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);
Here is the getBitmapFromView()
method
这是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;
}
It will display entire screen including content hidden in your ScrollView
UPDATED AS ON 20-04-2016
There is another better way to take screenshot.
Here I have taken screenshot of WebView
.
它将显示整个屏幕,包括隐藏在ScrollView
2016 年 4 月 20 日更新中的内容
还有另一种更好的截屏方法。
这里我截取了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);
}
});
Hope this helps..!
希望这可以帮助..!
回答by Ducky Chen
For newer Android platforms, one can execute a system utility screencap
in /system/bin
to get the screenshot without root permission.
You can try /system/bin/screencap -h
to see how to use it under adb or any shell.
对于较新的 Android 平台,可以screencap
在/system/bin
没有 root 权限的情况下执行系统实用程序来获取屏幕截图。你可以尝试/system/bin/screencap -h
看看如何在 adb 或任何 shell 下使用它。
By the way, I think this method is only good for single snapshot. If we want to capture multiple frames for screen play, it will be too slow. I don't know if there exists any other approach for a faster screen capture.
顺便说一句,我认为这种方法仅适用于单个快照。如果我们要捕捉多个帧用于屏幕播放,它会太慢。我不知道是否有任何其他方法可以更快地捕获屏幕。
回答by Ryan Conrad
AFAIK, All of the methods currently to capture a screenshot of android use the /dev/graphics/fb0 framebuffer. This includes ddms. It does require root to read from this stream. ddms uses adbd to request the information, so root is not required as adb has the permissions needed to request the data from /dev/graphics/fb0.
AFAIK,当前捕获android屏幕截图的所有方法都使用/dev/graphics/fb0 framebuffer。这包括 ddms。它确实需要 root 才能从此流中读取。ddms 使用 adbd 来请求信息,因此不需要 root,因为 adb 具有从 /dev/graphics/fb0 请求数据所需的权限。
The framebuffer contains 2+ "frames" of RGB565 images. If you are able to read the data, you would have to know the screen resolution to know how many bytes are needed to get the image. each pixel is 2 bytes, so if the screen res was 480x800, you would have to read 768,000 bytes for the image, since a 480x800 RGB565 image has 384,000 pixels.
帧缓冲区包含 2 个以上的 RGB565 图像“帧”。如果您能够读取数据,则必须知道屏幕分辨率才能知道获取图像需要多少字节。每个像素为 2 个字节,因此如果屏幕分辨率为 480x800,则您必须为图像读取 768,000 个字节,因为 480x800 RGB565 图像有 384,000 个像素。
回答by Pekka Nikander
[Based on Android source code:]
[基于Android源代码:]
At the C++ side, the SurfaceFlinger implements the captureScreen API. This is exposed over the binder IPC interface, returning each time a new ashmem area that contains the raw pixels from the screen. The actual screenshot is taken through OpenGL.
在 C++ 端,SurfaceFlinger 实现了 captureScreen API。这是通过 binder IPC 接口公开的,每次返回一个新的 ashmem 区域,其中包含来自屏幕的原始像素。实际截图是通过 OpenGL 截取的。
For the system C++ clients, the interface is exposed through the ScreenshotClient class, defined in <surfaceflinger_client/SurfaceComposerClient.h>
for Android < 4.1; for Android > 4.1 use <gui/SurfaceComposerClient.h>
对于系统 C++ 客户端,该接口通过 ScreenshotClient 类公开,在<surfaceflinger_client/SurfaceComposerClient.h>
Android < 4.1 中定义;适用于 Android > 4.1 使用<gui/SurfaceComposerClient.h>
Before JB, to take a screenshot in a C++ program, this was enough:
在 JB 之前,要在 C++ 程序中截取屏幕截图,这就足够了:
ScreenshotClient ssc;
ssc.update();
With JB and multiple displays, it becomes slightly more complicated:
使用 JB 和多个显示器,它变得稍微复杂一些:
ssc.update(
android::SurfaceComposerClient::getBuiltInDisplay(
android::ISurfaceComposer::eDisplayIdMain));
Then you can access it:
然后你可以访问它:
do_something_with_raw_bits(ssc.getPixels(), ssc.getSize(), ...);
Using the Android source code, you can compile your own shared library to access that API, and then expose it through JNI to Java. To create a screen shot form your app, the app has to have the READ_FRAME_BUFFER
permission.
But even then, apparently you can create screen shots only from system applications, i.e. ones that are signed with the same key as the system. (This part I still don't quite understand, since I'm not familiar enough with the Android Permissions system.)
使用 Android 源代码,您可以编译自己的共享库以访问该 API,然后通过 JNI 将其公开给 Java。要从您的应用程序创建屏幕截图,该应用程序必须具有READ_FRAME_BUFFER
权限。但即便如此,显然您只能从系统应用程序(即使用与系统相同的密钥签名的应用程序)创建屏幕截图。(这部分我还是不太明白,因为我对Android权限系统还不够熟悉。)
Here is a piece of code, for JB 4.1 / 4.2:
这是一段代码,用于 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;
}
回答by Kuba
You can try the following library: Android Screenshot Library (ASL)enables to programmatically capture screenshots from Android devices without requirement of having root access privileges. Instead, ASL utilizes a native service running in the background, started via the Android Debug Bridge (ADB) once per device boot.
您可以尝试以下库:Android 屏幕截图库 (ASL)能够以编程方式从 Android 设备捕获屏幕截图,而无需具有 root 访问权限。相反,ASL 使用在后台运行的本机服务,每次设备启动时通过 Android 调试桥 (ADB) 启动一次。
回答by Rui Marques
Framebuffer seems the way to go, it will not always contain 2+ frames like mentioned by Ryan Conrad. In my case it contained only one. I guess it depends on the frame/display size.
Framebuffer 似乎是要走的路,它不会总是包含 2+ 帧,就像 Ryan Conrad 提到的那样。就我而言,它只包含一个。我想这取决于框架/显示尺寸。
I tried to read the framebuffer continuously but it seems to return for a fixed amount of bytes read. In my case that is (3 410 432) bytes, which is enough to store a display frame of 854*480 RGBA (3 279 360 bytes). Yes, the frame in binary outputed from fb0 is RGBA in my device. This will most likely depend from device to device. This will be important for you to decode it =)
我试图连续读取帧缓冲区,但它似乎返回读取固定数量的字节。在我的情况下,这是 (3 410 432) 字节,足以存储 854*480 RGBA(3 279 360 字节)的显示帧。是的,从 fb0 输出的二进制帧在我的设备中是 RGBA。这很可能取决于设备。这对您解码很重要 =)
In my device /dev/graphics/fb0 permissions are so that only rootand users from group graphicscan readthe fb0. graphicsis a restricted group so you will probably only access fb0 with a rooted phone using su command.
在我的设备中 /dev/graphics/fb0 权限是这样的,只有 root和来自图形组的用户才能读取fb0。graphics是一个受限制的组,因此您可能只能使用 su 命令通过 root 手机访问 fb0。
Android apps have the user id (uid) app_##and group id (guid) app_##.
Android 应用程序具有用户 ID (uid) app_##和组 ID (guid) app_##。
adb shell has uid shelland guid shell, which has much more permissions than an app. You can actually check those permissions at /system/permissions/platform.xml
adb shell 有uid shell和guid shell,它们比应用程序拥有更多的权限。您实际上可以在/system/permissions/platform.xml 中检查这些权限
This means you will be able to read fb0 in the adb shell without root but you will not read it within the app without root.
这意味着您将能够在没有 root 的情况下在 adb shell 中读取 fb0,但在没有 root 的情况下您将无法在应用程序中读取它。
Also, giving READ_FRAME_BUFFER and/or ACCESS_SURFACE_FLINGER permissions on AndroidManifest.xml will do nothing for a regular app because these will only work for 'signature' apps.
此外,在 AndroidManifest.xml 上授予 READ_FRAME_BUFFER 和/或 ACCESS_SURFACE_FLINGER 权限对常规应用程序没有任何作用,因为这些仅适用于“签名”应用程序。
回答by zed_0xff
if you want to do screen capture from Java code in Android app AFAIK you must have Root provileges.
如果您想从 Android 应用程序 AFAIK 中的 Java 代码进行屏幕截图,您必须拥有 Root 权限。