web-dev-qa-db-ja.com

Android画面表示のスクリーンショットを撮ると黒い画面が表示される

コードでゲームのスクリーンショットを撮り、インテントで共有しようとしています。これらのことはできますが、スクリーンショットは常に黒く表示されます。スクリーンショットの共有に関連するコードは次のとおりです。

View view = MainActivity.getView();
view.setDrawingCacheEnabled(true);
Bitmap screen = Bitmap.createBitmap(view.getDrawingCache(true));
.. save Bitmap

これはMainActivityにあります。

view = new GameView(this);
view.setLayoutParams(new RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.FILL_PARENT,
            RelativeLayout.LayoutParams.FILL_PARENT));

public static SurfaceView getView() {
    return view;
}

そしてビュー自体:

public class GameView extends SurfaceView implements SurfaceHolder.Callback {
private static SurfaceHolder surfaceHolder;
...etc

そして、これは私がすべてを描く方法です:

Canvas canvas = surfaceHolder.lockCanvas(null);
        if (canvas != null) {
                Game.draw(canvas);
...

わかりました、いくつかの答えに基づいて、私はこれを構築しました:

public static void share() {
    Bitmap screen = GameView.SavePixels(0, 0, Screen.width, Screen.height);
    Calendar c = Calendar.getInstance();
    Date d = c.getTime();
    String path = Images.Media.insertImage(
            Game.context.getContentResolver(), screen, "screenShotBJ" + d
                    + ".png", null);
    System.out.println(path + " PATH");
    Uri screenshotUri = Uri.parse(path);
    final Intent emailIntent = new Intent(
            Android.content.Intent.ACTION_SEND);
    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    emailIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
    emailIntent.setType("image/png");
    Game.context.startActivity(Intent.createChooser(emailIntent,
            "Share High Score:"));
}

Gameviewには次のメソッドが含まれています。

public static Bitmap SavePixels(int x, int y, int w, int h) {
    EGL10 egl = (EGL10) EGLContext.getEGL();
    GL10 gl = (GL10) egl.eglGetCurrentContext().getGL();
    int b[] = new int[w * (y + h)];
    int bt[] = new int[w * h];
    IntBuffer ib = IntBuffer.wrap(b);
    ib.position(0);
    gl.glReadPixels(x, 0, w, y + h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
    for (int i = 0, k = 0; i < h; i++, k++) {
        for (int j = 0; j < w; j++) {
            int pix = b[i * w + j];
            int pb = (pix >> 16) & 0xff;
            int pr = (pix << 16) & 0x00ff0000;
            int pix1 = (pix & 0xff00ff00) | pr | pb;
            bt[(h - k - 1) * w + j] = pix1;
        }
    }

    Bitmap sb = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
    return sb;
}

スクリーンショットはまだ黒です。おそらく保存方法に何か問題がありますか?

スクリーンショットを撮るためにいくつかの異なる方法を試しましたが、どれも機能しませんでした。上記のコードに示されている方法は、最も一般的に推奨される方法です。しかし、うまくいかないようです。これはSurfaceViewの使用に関する問題ですか?もしそうなら、なぜ私はそれを使用できないのにview.getDrawingCache(true)が存在するのですか?これをどのように修正しますか?

私のコード:

public static void share() {
    // GIVES BLACK SCREENSHOT:
    Calendar c = Calendar.getInstance();
    Date d = c.getTime();

    Game.update();
    Bitmap.Config conf = Bitmap.Config.RGB_565;
    Bitmap image = Bitmap.createBitmap(Screen.width, Screen.height, conf);
    Canvas canvas = GameThread.surfaceHolder.lockCanvas(null);
    canvas.setBitmap(image);
    Paint backgroundPaint = new Paint();
    backgroundPaint.setARGB(255, 40, 40, 40);
    canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(),
            backgroundPaint);
    Game.draw(canvas);
    Bitmap screen = Bitmap.createBitmap(image, 0, 0, Screen.width,
            Screen.height);
    canvas.setBitmap(null);
    GameThread.surfaceHolder.unlockCanvasAndPost(canvas);

    String path = Images.Media.insertImage(
            Game.context.getContentResolver(), screen, "screenShotBJ" + d
                    + ".png", null);
    System.out.println(path + " PATH");
    Uri screenshotUri = Uri.parse(path);
    final Intent emailIntent = new Intent(
            Android.content.Intent.ACTION_SEND);
    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    emailIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
    emailIntent.setType("image/png");
    Game.context.startActivity(Intent.createChooser(emailIntent,
            "Share High Score:"));
}

ありがとうございました。

28
TastyLemons

これについてはかなりの混乱があり、いくつかの 正しいanswers があります。

取引は次のとおりです。

  1. SurfaceViewには、SurfaceとViewの2つの部分があります。サーフェスは、すべてのビューUI要素から完全に独立したレイヤー上にあります。 getDrawingCache()アプローチは、Viewレイヤーでのみ機能するため、Surfaceでは何もキャプチャしません。

  2. バッファキューには、プロデューサ-コンシューマAPIがあり、プロデューサは1つしか持てません。 Canvasはプロデューサー、GLESは別のプロデューサーです。 Canvasで描画したり、GLESでピクセルを読み取ったりすることはできません。 (技術的には、CanvasがGLESを使用しており、ピクセルを読み取ろうとしたときに正しいEGLコンテキストが最新であった場合、couldが保証されません。SurfaceへのCanvasレンダリングは保証されませんAndroidのどのリリースバージョンでも加速されるため、現時点では動作する見込みはありません。)

  3. (あなたのケースには関係ありませんが、完全を期すために言及します:) Surfaceはフレームバッファーではなく、バッファーのキューです。 GLESを使用してバッファを送信すると、バッファはなくなり、そこから読み取ることができなくなります。したがって、GLESでレンダリングし、GLESでキャプチャしている場合、eglSwapBuffers()を呼び出す前にピクセルを読み戻す必要があります。

Canvasレンダリングでは、Surfaceコンテンツを「キャプチャ」する最も簡単な方法は、単純に2回描画することです。画面サイズのビットマップを作成し、ビットマップからキャンバスを作成し、draw()関数に渡します。

GLESレンダリングでは、バッファスワップの前にglReadPixels()を使用してピクセルを取得できます。 Grafika ;にグラブコードの実装(問題のコードよりも安価)があります。 EglSurfaceBasesaveFrame()を参照してください。

ビデオをSurfaceに(MediaPlayerを介して)直接送信する場合、アプリはフレームにアクセスできないため、フレームをキャプチャする方法はありません。メディアサーバーからコンポジター(SurfaceFlinger)に直接移動します。ただし、SurfaceTextureを介して着信フレームをルーティングし、表示用とキャプチャ用に1回ずつ、アプリから2回レンダリングできます。詳細については、 この質問 を参照してください。

1つの方法は、SurfaceViewをTextureViewに置き換えることです。TextureViewは、他のSurfaceと同じように描画できます。その後、 getBitmap() 呼び出しのいずれかを使用して、フレームをキャプチャできます。 TextureViewはSurfaceViewよりも効率が低いため、これはすべての状況に推奨されるわけではありませんが、簡単に実行できます。

SurfaceコンテンツとView UIコンテンツの両方を含む合成スクリーンショットを取得したい場合は、上記のようにCanvasをキャプチャし、通常の描画キャッシュトリックでViewをキャプチャしてから、2つを手動で合成する必要があります。これはシステムパーツ(ステータスバー、ナビゲーションバー)を取得しないことに注意してください。

Update:Lollipop以降(API 21以降)では、 MediaProjection クラスを使用して、仮想画面で画面全体をキャプチャできます。表示。このアプローチにはいくつかのトレードオフがあります。 Surfaceに送信されたフレームではなく、レンダリングされた画面をキャプチャしているため、取得したものはウィンドウに合わせて拡大または縮小されている可能性があります。さらに、ProjectionManagerオブジェクトでcreateScreenCaptureIntentを呼び出してインテントを作成し、その結果を待つ必要があるため、このアプローチにはアクティビティスイッチが含まれます。

これらすべての仕組みの詳細については、Android System-Level Graphics Architecture のドキュメントをご覧ください。

55
fadden

返信が遅いことは知っていますが、同じ問題に直面している人にとっては、

スナップショットを取得するためにPixelCopyを使用できます。 API level 24以上

PixelCopy.request(surfaceViewObject,BitmapDest,listener,new Handler());

ここで、

surfaceViewObjectは表面ビューのオブジェクトです

BitmapDestは、イメージが保存されるビットマップオブジェクトで、nullにはできません

リスナーはOnPixelCopyFinishedListener

詳細については、参照してください- https://developer.Android.com/reference/Android/view/PixelCopy

2
Anjani Mittal

これは、SurfaceViewがOpenGLスレッドを使用して描画し、ハードウェアバッファーに直接描画するためです。 glReadPixels(およびおそらくGLWrapper)を使用する必要があります。

スレッドを参照してください: Android OpenGLスクリーンショット

2
Zielony