0) 声明
本文是根据 @goodbranch 的《Android 5.0及以上实现屏幕截图 》,进行修改过之后,整理出来的。因作者未回复私信,加之在不 root 的前提下用代码实现截屏着实不易,为了让更多的人少走弯路,故编写此文。如有冒犯,请及时提醒,以便删除此文。
地址 : http://blog.csdn.net/consumer11/article/details/51967340
1) MainActivity 类
⑴ 声明一个常量
private static final int REQUEST_CODE = 1;
1
⑵ 新建一个按钮,并添加点事件
???.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
requestCapturePermission();
}
});
1
2
3
4
5
6
⑶ 实例化 MediaProjectionManager
private void requestCapturePermission() {
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
}
1
2
3
4
⑷ 在 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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}
}
}
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
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";
}
}
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
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() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}
1
2
3
4
5
6
7
6) 添加SD卡写入权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
1
7) 关于悬浮窗权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
1
⑴ 定义一个常量
int PERMISSION_CODE = 999;
1
⑵ 写一个检查权限的方法,在程序初始化时调用
@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); // 调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
⑶ 在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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
8) Tips
已针对 LogCat 报出的异常,进行了修复。
能够用代码在不 root 的前提下实现截屏,着实不易。希望爱钻研的你们能够实现:在低版本的、没有 root 过的安卓手机上用代码实现截屏的功能。
继续秉承拿来就能的原则!