安卓API21及以上版本,利用 MediaProjectionManager 截屏

    xiaoxiao2021-03-25  65

    0) 声明

    本文是根据 @goodbranch 的《Android 5.0及以上实现屏幕截图 》,进行修改过之后,整理出来的。因作者未回复私信,加之在不 root 的前提下用代码实现截屏着实不易,为了让更多的人少走弯路,故编写此文。如有冒犯,请及时提醒,以便删除此文。 地址 : http://blog.csdn.net/consumer11/article/details/51967340

    1) MainActivity 类

    ⑴ 声明一个常量

    private static final int REQUEST_CODE = 1;

    ⑵ 新建一个按钮,并添加点事件

    ???.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { requestCapturePermission(); } });

    ⑶ 实例化 MediaProjectionManager

    private void requestCapturePermission() { MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE); }

    ⑷ 在 onActivityResult 中传入参数

    @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_CODE: if (null != data) { if (RESULT_OK == resultCode) { ScreenShot.setUpMediaProjection(MainActivity.this, data); ScreenShot.getWH(MainActivity.this); ScreenShot.createImageReader(); ScreenShot.beginScreenShot(MainActivity.this, data); } } break; } }

    2) ScreenShot 类

    import android.app.Activity; import android.content.Context; import android.content.Intent; 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.os.Handler; import android.util.DisplayMetrics; import android.view.Surface; import android.view.WindowManager;

    public class ScreenShot {

    private static WindowManager windowManager; private static int screenDensity; private static int screenWidth; private static int screenHeight; private static MediaProjection mediaProjection; private static VirtualDisplay virtualDisplay; private static ImageReader imageReader; public static Surface surface; public static void setUpMediaProjection(Activity activity, Intent scIntent) { if (scIntent == null) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); activity.startActivity(intent); } else { mediaProjection = getMediaProjectionManager(activity).getMediaProjection(Activity.RESULT_OK, scIntent); } } public static void getWH(Activity activity) { DisplayMetrics metrics = new DisplayMetrics(); windowManager = (WindowManager) activity.getSystemService(activity.WINDOW_SERVICE); windowManager.getDefaultDisplay().getMetrics(metrics); screenDensity = metrics.densityDpi; screenWidth = metrics.widthPixels; screenHeight = metrics.heightPixels; } public static void createImageReader() { imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 1); } public static void beginScreenShot(final Activity activity, final Intent intent) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { beginVirtual(activity, intent); } }, 0); handler.postDelayed(new Runnable() { @Override public void run() { beginCapture(activity, intent); } }, 150); } private static void beginVirtual(Activity activity, Intent intent) { if (null != mediaProjection) { virtualDisplay(); } else { setUpMediaProjection(activity, intent); virtualDisplay(); } } private static void virtualDisplay() { surface = imageReader.getSurface(); virtualDisplay = mediaProjection.createVirtualDisplay("screen-mirror", screenWidth, screenHeight, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null); } private static MediaProjectionManager getMediaProjectionManager(Activity activity) { return (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE); } private static void beginCapture(Activity activity, Intent intent) { Image acquireLatestImage = null; try { acquireLatestImage = imageReader.acquireLatestImage(); } catch (IllegalStateException e) { if (null != acquireLatestImage) { acquireLatestImage.close(); acquireLatestImage = null; acquireLatestImage = imageReader.acquireLatestImage(); } } if (acquireLatestImage == null) { beginScreenShot(activity, intent); } else { SaveTask saveTask = new SaveTask(); AsyncTaskCompat.executeParallel(saveTask, acquireLatestImage); new Handler().postDelayed(new Runnable() { @Override public void run() { releaseVirtual(); stopMediaProjection(); } }, 1000); } } private static void releaseVirtual() { if (null != virtualDisplay) { virtualDisplay.release(); virtualDisplay = null; } } private static void stopMediaProjection() { if (null != mediaProjection) { mediaProjection.stop(); mediaProjection = null; } } }

    3) SaveTask 类

    import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale;

    import android.graphics.Bitmap; import android.media.Image; import android.media.Image.Plane; import android.os.AsyncTask; import android.os.Environment;

    public class SaveTask extends AsyncTask<Image, Void, Bitmap> {

    @Override protected Bitmap doInBackground(Image... args) { if (null == args || 1 > args.length || null == args[0]) { return null; } Image image = args[0]; int width; int height; try { width = image.getWidth(); height = image.getHeight(); } catch (IllegalStateException e) { return null; } final Plane[] planes = image.getPlanes(); final ByteBuffer buffer = planes[0].getBuffer(); // 每个像素的间距 int pixelStride = planes[0].getPixelStride(); // 总的间距 int rowStride = planes[0].getRowStride(); int rowPadding = rowStride - pixelStride * width; Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer); bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height); image.close(); File fileImage = null; if (null != bitmap) { FileOutputStream fos = null; try { fileImage = new File(createFile()); if (!fileImage.exists()) { fileImage.createNewFile(); fos = new FileOutputStream(fileImage); bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); } } catch (IOException e) { fileImage = null; } finally { if (null != fos) { try { fos.close(); } catch (IOException e) { } } if (null != bitmap && !bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; } } } if (null != fileImage) { return bitmap; } return null; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (ScreenShot.surface.isValid()) { ScreenShot.surface.release(); } } // 输出目录 private String createFile() { String outDir = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US); String date = simpleDateFormat.format(new Date()); return outDir + date + ".png"; } }

    4) AsyncTaskCompat 类

    import android.os.AsyncTask;

    public final class AsyncTaskCompat {

    @SafeVarargs public static <Params, Progress, Result> AsyncTask<Params, Progress, Result> executeParallel( AsyncTask<Params, Progress, Result> task, Params... params) { if (task == null) { throw new IllegalArgumentException("task can not be null"); } AsyncTaskCompatHoneycomb.executeParallel(task, params); return task; } private AsyncTaskCompat() { } }

    5) AsyncTaskCompatHoneycomb 类

    import android.os.AsyncTask;

    class AsyncTaskCompatHoneycomb {

    @SafeVarargs static <Params, Progress, Result> void executeParallel(AsyncTask<Params, Progress, Result> task, Params... params) { task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); } }

    6) 添加SD卡写入权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    7) 关于悬浮窗权限

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    ⑴ 定义一个常量

    int PERMISSION_CODE = 999;

    ⑵ 写一个检查权限的方法,在程序初始化时调用

    @SuppressLint({ "NewApi", "InlinedApi" }) private boolean checkPermission(Activity activity) { if (Settings.canDrawOverlays(activity)) { return true; } Toast.makeText(activity, "请开启【允许在其他应用的上层显示】权限!", Toast.LENGTH_LONG).show(); Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.getPackageName())); activity.startActivityForResult(intent, PERMISSION_CODE); return false; } checkPermission(MainActivity.this); // 调用

    ⑶ 在MainActivity的onActivityResult(int requestCode, int resultCode, Intent data) 方法中检查回调

    if (PERMISSION_CODE == resultCode) { if (!Settings.canDrawOverlays(this)) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("提示").setMessage("授权失败,无法使用程序").setCancelable(false) .setNegativeButton("退出程序", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { System.exit(0); } }) .create().show(); } else { Toast.makeText(MainActivity.this, "授权成功!", Toast.LENGTH_SHORT).show(); requestCapturePermission(); } }

    8) Tips

    已针对 LogCat 报出的异常,进行了修复。能够用代码在不 root 的前提下实现截屏,着实不易。希望爱钻研的你们能够实现:在低版本的、没有 root 过的安卓手机上用代码实现截屏的功能。继续秉承拿来就能的原则!
    转载请注明原文地址: https://ju.6miu.com/read-35836.html

    最新回复(0)