如何捕获android设备屏幕内容?

时间:2010-06-18 06:22:35

标签: android screen-capture

  

可能重复:
  How to programatically take a screenshot on Android?

如何使用快照数据捕获Android设备屏幕内容并制作图像文件?我应该使用哪种API?在哪里可以找到相关资源?

顺便说一句: 不是相机快照,而是设备屏幕

8 个答案:

答案 0 :(得分:43)

使用以下代码:

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中的内容

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

希望这会有所帮助..!

答案 1 :(得分:23)

AFAIK,目前捕获android截图的所有方法都使用/ dev / graphics / fb0 framebuffer。这包括ddms。它确实需要root才能从此流中读取。 ddms使用adbd来请求信息,因此不需要root,因为adb具有从/ dev / graphics / fb0请求数据所需的权限。

帧缓冲包含2个“帧”的RGB565图像。如果您能够读取数据,则必须知道屏幕分辨率才能知道获取图像需要多少字节。每个像素为2个字节,因此如果屏幕分辨率为480x800,则必须为该图像读取768,000个字节,因为480x800 RGB565图像具有384,000个像素。

答案 2 :(得分:23)

对于较新的Android平台,可以在screencap中执行系统实用程序/system/bin,以获取没有root权限的屏幕截图。 您可以尝试/system/bin/screencap -h查看如何在adb或任何shell下使用它。

顺便说一句,我认为这种方法只适用于单个快照。 如果我们想要捕获多个帧以进行屏幕播放,那么它将会太慢。 我不知道是否存在更快的屏幕捕获方法。

答案 3 :(得分:17)

[基于Android源代码:]

在C ++方面,SurfaceFlinger实现了captureScreen API。这通过活页夹IPC界面公开,每次返回包含来自屏幕的原始像素的新ashmem区域。实际屏幕截图是通过OpenGL进行的。

对于系统C ++客户端,该接口通过ScreenshotClient类公开,该类在<surfaceflinger_client/SurfaceComposerClient.h> for Android&lt; 4.1;对于Android&gt; 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;
}

答案 4 :(得分:15)

您可以尝试使用以下库:Android Screenshot Library (ASL)可以编程方式从Android设备捕获屏幕截图,而无需具有root访问权限。相反,ASL利用在后台运行的本机服务,每次设备启动时通过Android调试桥(ADB)启动。

答案 5 :(得分:12)

根据this link,可以在android sdk的tools目录中使用ddms进行屏幕截图。

要在应用程序中执行此操作(而不是在开发期间),还有一些应用程序可以执行此操作。但正如@ zed_0xff所指出的那样,肯定需要root。

答案 6 :(得分:6)

Framebuffer似乎要走了,它并不总是包含像Ryan Conrad所提到的2+帧。就我而言,它只包含一个。我想这取决于帧/显示尺寸。

我试图连续读取帧缓冲区,但似乎返回固定数量的字节读取。在我的情况下是(3 410 432)字节,这足以存储854 * 480 RGBA(3 279 360字节)的显示帧。是的,从fb0输出的二进制帧是我设备中的RGBA 这很可能取决于设备与设备。这对你解码它很重要=)

在我的设备/ dev / graphics / fb0中,权限是仅root 组图形的用户可以读取 fb0。 图形是一个受限制的组,因此您可能只能使用su命令使用root电话访问fb0。

Android应用的用户ID为(uid)app _ ## ,组ID为(guid)app _ ##

adb shell有 uid shell guid shell ,它拥有比应用更多的权限。 您实际上可以在 /system/permissions/platform.xml

中检查这些权限

这意味着您可以在没有root的情况下在adb shell中读取fb0,但是如果没有root,您将无法在应用中读取它。

此外,在AndroidManifest.xml上提供READ_FRAME_BUFFER和/或ACCESS_SURFACE_FLINGER权限对常规应用程序无效,因为这些仅适用于“签名”应用程序。

答案 7 :(得分:5)

如果你想从Android应用程序AFAIK中的Java代码进行屏幕截图,你必须有Root的权限。