web-dev-qa-db-ja.com

Androidデバイス画面のコンテンツをキャプチャするには?

可能性のある複製:
Androidでプログラムでスクリーンショットを撮る方法

Androidデバイス画面コンテンツをキャプチャし、スナップショットデータを使用して画像ファイルを作成する方法は?どのAPIを使用する必要がありますか、関連リソースはどこにありますか?

ところで:カメラのスナップショットではなく、デバイス画面

49
Jagie

このリンク によると、Android sdkのtoolsディレクトリでddmsを使用して画面キャプチャを取得することが可能です。

(開発中ではなく)アプリケーション内でこれを行うには、そうするアプリケーションもあります。しかし、@ zed_0xffが指摘しているように、確かにrootが必要です。

13
Joubarc

次のコードを使用します。

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

お役に立てれば..!

43
Nirav Dangi

新しいAndroidプラットフォームの場合、システムユーティリティscreencap in /system/binを実行して、ルート権限なしでスクリーンショットを取得できます。/system/bin/screencap -hを試してみるadbまたは任意のシェルで使用する方法。

ところで、この方法は単一のスナップショットにのみ適していると思います。スクリーンプレイ用に複数のフレームをキャプチャする場合は、速度が遅すぎます。より高速な画面キャプチャのための他のアプローチが存在するかどうかはわかりません。

23
Ducky Chen

知る限り、現在Androidのスクリーンショットをキャプチャするすべてのメソッドは、/ dev/graphics/fb0フレームバッファを使用します。これにはddmsが含まれます。このストリームから読み取るにはrootが必要です。情報を要求するため、adbには/ dev/graphics/fb0からデータを要求するために必要な権限があるため、rootは必要ありません。

フレームバッファには、RGB565イメージの2+「フレーム」が含まれています。データを読み取ることができる場合は、画像を取得するために必要なバイト数を知るために画面解像度を知る必要があります。各ピクセルは2バイトです。したがって、画面解像度が480x800の場合、480x800 RGB565イメージは384,000ピクセルなので、イメージの768,000バイトを読み取る必要があります。

23
Ryan Conrad

[Androidソースコード:]に基づく)

C++側では、SurfaceFlingerはcaptureScreen APIを実装します。これはバインダーIPCインターフェースで公開され、毎回画面からの生のピクセルを含む新しいashmem領域を返します。実際のスクリーンショットはOpenGLを通して取得されます。

システムC++クライアントの場合、インターフェースはScreenshotClientクラスを通じて公開され、<surfaceflinger_client/SurfaceComposerClient.h> for Android <4.1; for Android> 4.1 use <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許可。しかし、それでも、システムアプリケーション、つまりシステムと同じキーで署名されたものからのみスクリーンショットを作成できるようです(この部分はまだ理解できません。 m 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;
}
18
Pekka Nikander

次のライブラリを試すことができます。 Androidスクリーンショットライブラリ(ASL) Androidルートアクセス権限を必要とせずにデバイスからスクリーンショットをキャプチャできるようにします。代わりに、ASLはバックグラウンドで実行されるネイティブサービス。デバイスの起動ごとに1回、Android Debug Bridge(ADB)を介して開始されます。

15
Kuba

Ryan Conradが言及したように、フレームバッファが道のりのように見え、常に2+フレームを含むとは限りません。私の場合は、1つしか含まれていません。フレーム/ディスプレイのサイズに依存すると思います。

フレームバッファーを連続して読み取ろうとしましたが、一定量のバイトが返されたようです。私の場合、これは(3 410 432)バイトで、854 * 480 RGBA(3 279 360バイト)の表示フレームを保存するのに十分です。はい、fb0から出力されるバイナリのフレームは、私のデバイスではRGBAですこれはほとんどの場合、デバイスによって異なります。これは、デコードすることが重要になります=)

私のデバイスでは、/ dev/graphics/fb0のパーミッションはrootのみおよびgroup graphicsのユーザーはread fb0になります。 graphicsは制限されたグループなので、おそらくsuコマンドを使用してルート化された電話でのみfb0にアクセスします。

Androidアプリには、ユーザーID (uid)app _ ##とグループID (guid)app _ ##があります。

adb Shellにはid Shellguid Shellがあり、アプリよりもはるかに多くの権限があります。 / system/permissions/platform.xmlで実際にこれらのパーミッションを確認できます

つまり、ルートなしでadbシェルでfb0を読み取ることができますが、ルートなしでアプリ内でfb0を読み取ることはできません。

また、AndroidManifest.xmlでREAD_FRAME_BUFFERおよび/またはACCESS_SURFACE_FLINGERパーミッションを付与しても、通常のアプリでは「署名」アプリでのみ機能するため、何も実行されません。

7
Rui Marques

Java code in Android app AFAIKからスクリーンキャプチャを行いたい場合は、ルートprovilegesが必要です。

5
zed_0xff