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

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

  1. 已针对 LogCat 报出的异常,进行了修复。
  2. 能够用代码在不 root 的前提下实现截屏,着实不易。希望爱钻研的你们能够实现:在低版本的、没有 root 过的安卓手机上用代码实现截屏的功能。
  3. 继续秉承拿来就能的原则!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值