端末で「電源ボタン」+「音量下」の同時押しでスクリーンショットが撮れますがタイミングが合いにくかったり、プログラムで画面のショットを取りたいときがありますが、方法は色々可能です。
Android 9.0
Screen Capture
注)このスクリーンショットはAndroid 10では以下のようなwarningが出ます。
これはAndroid Studioに入っているSampleを元に切り出したものですけれど…
1 2 |
Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION |
というエラーが出ているので、Foreground Serviceに変更しないといけないようで、
getMediaProjection()の前にstartforeground()でサービスに入る実装になります。
Android 9.0までは似たようなダイアログが出るだけで済むのですが、厳しくなったようですね。
アプリでスクリーンショット
以下はAPI28まで使えるサンプルコードです。
adb コマンドでのキャプチャはまだできます。
~~~~~~~~~~~~~~~~~~~~~
Android Studioから「New」「Import Sample」のサンプルの中に「Screen Capture」があります。このサンプルはFragmentになっているのでActivityに落として簡略化してImageViewで表示させるように修正しました。
アプリでスクリーンショットをユーザーに無断で撮るのは、ユーザーの重要な情報も取得できてしまうため許可を得る工程が入ります。
この画像をアプリの背景とし、そのスクリーンショットを取って画面上に表示させてみます。
MediaProjection を使っていますが、Andorid5.0以降の機能です。
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
package your.package.name; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.Image; import android.media.ImageReader; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import java.nio.ByteBuffer; public class MainActivity extends AppCompatActivity { private MediaProjectionManager mpManager; private MediaProjection mProjection; private static final int REQUEST_MEDIA_PROJECTION = 1001; private int displayWidth, displayHeight; private ImageReader imageReader; private VirtualDisplay virtualDisplay; private int screenDensity; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); // ボタンタップでスクリーンショットを撮る button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { getScreenshot(); } }); // 撮影したスクリーンを表示するImageView imageView = findViewById(R.id.image_view); // 画面の縦横サイズとdpを取得 DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); screenDensity = displayMetrics.densityDpi; displayWidth = displayMetrics.widthPixels; displayHeight = displayMetrics.heightPixels; mpManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE); // permissionを確認するintentを投げ、ユーザーの許可・不許可を受け取る if(mpManager != null){ startActivityForResult(mpManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION); } } // ユーザーの許可を受け取る @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (REQUEST_MEDIA_PROJECTION == requestCode) { if (resultCode != RESULT_OK) { // 拒否された Toast.makeText(this, "User cancelled", Toast.LENGTH_LONG).show(); return; } // 許可された結果を受け取る setUpMediaProjection(resultCode, data); } } private void setUpMediaProjection(int code, Intent intent) { mProjection = mpManager.getMediaProjection(code, intent); setUpVirtualDisplay(); } private void setUpVirtualDisplay() { imageReader = ImageReader.newInstance( displayWidth, displayHeight, PixelFormat.RGBA_8888, 2); virtualDisplay = mProjection.createVirtualDisplay("ScreenCapture", displayWidth, displayHeight, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, imageReader.getSurface(), null, null); } private void getScreenshot() { // ImageReaderから画面を取り出す Log.d("debug", "getScreenshot"); Image image = imageReader.acquireLatestImage(); Image.Plane[] planes = image.getPlanes(); ByteBuffer buffer = planes[0].getBuffer(); int pixelStride = planes[0].getPixelStride(); int rowStride = planes[0].getRowStride(); int rowPadding = rowStride - pixelStride * displayWidth; // バッファからBitmapを生成 Bitmap bitmap = Bitmap.createBitmap( displayWidth + rowPadding / pixelStride, displayHeight, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer); image.close(); imageView.setImageBitmap(bitmap); } @Override protected void onDestroy() { if (virtualDisplay != null) { Log.d("debug","release VirtualDisplay"); virtualDisplay.release(); } super.onDestroy(); } } |
テストするための画像img.jpgを探して入れておきます。
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@drawable/img" tools:context=".MainActivity"> <Button android:id="@+id/button" android:layout_gravity="center_horizontal" android:layout_margin="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button" /> <ImageView android:id="@+id/image_view" android:layout_width="300dp" android:layout_height="wrap_content" android:contentDescription="@string/description"/> </LinearLayout> |
strings.xml
1 2 3 4 5 |
<resources> <string name="app_name">YourAppName</string> <string name="button">Button</string> <string name="description">soccer girl</string> </resources> |
スクリーンショットが撮れました。
この画像を端末内に保存してギャラリーで見たり、メールで送ったり、をPCに取り込んだりすることが可能です
adb コマンドを使ってキャプチャする
adbコマンドからもキャプチャすることができます。これはAndroid 10でも今のところ使えます。
端末のディレクトリーで書き込み可能領域にtext.pngでスクリーンショット画像を撮って保存する例です。
1 |
>adb shell screencap -p /sdcard/image.png |
その後で、その画像をPCのc:\tempに取り込めばいいのでこういうコマンドでいけます。
1 2 3 4 5 |
>adb shell screencap -p /sdcard/image.png >adb pull /sdcard/image.png c:\temp [100%] /sdcard/image.png > |
adbコマンドについて、書き込み可能領域の確認はadb コマンドを参照してください。
emulatorでのスクリーンショット
Android Studioでアプリを実行、カメラアイコンの「Screen Capture」をクリックします
実行中のスクリーン表示されます
実行ターゲットによっては、大きすぎる場合もありますが、そんな時は虫眼鏡アイコンの「-」をクリックして画像を小さく表示します。ここで拡大・縮小はあくまで表示だけです。
だいぶ見やすくなりました。更にFrame Screenshotなどの効果も演出できます。
これで「Save」すると出来上がりです。