????????Android在5.0之后提供了官方的截屏API,再也不需要root再調(diào)用adb指令,或者使用輔助服務(wù)模擬截屏按鍵實現(xiàn)截屏了片挂。本文將介紹實現(xiàn)過程睬魂,并在最后提供Demo以供下載參考终吼。
1. 實現(xiàn)流程
1.1 獲取屏幕的實時信息
? 先請求截屏的服務(wù),需要等用戶同意后氯哮,才能獲取屏幕實時信息际跪。通過調(diào)用getSystemService()
方法,得到MediaProjectionManager對象喉钢。該對象我們需要關(guān)注兩個東西姆打,一個是通過createScreenCaptureIntent()
方法得到的Intent對象(這里會用到),一個是通過getMediaProjection()
得到的MediaProjection對象(下節(jié)會用到)肠虽。
? 在這里幔戏,我們通過MediaProjectionManager的createScreenCaptureIntent
方法得到一個Intent請求,將其作為startActivityForResult的參數(shù)啟動一個截屏請求税课。此時闲延,系統(tǒng)會向用戶申請截屏權(quán)限,告知用戶接下來會有截屏操作韩玩,同時也開始截屏的準(zhǔn)備工作垒玲。代碼實現(xiàn)如下:
private void try2StartScreenShot() {
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
}
? 在onActivityResult方法里面可以拿到返回的Intent數(shù)據(jù),該Intent只是一個引用啸如,里面的東西是實時在改變的(因為里面記錄的是屏幕信息)侍匙,信息存儲在intent里面的bundle,bundle里面記錄的是一個用Android專用序列化方式Parcelable序列化過的一個對象。代碼如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_MEDIA_PROJECTION:{
if (resultCode == RESULT_OK && data != null) {
this.data = data;
}
break;
}
}
}
1.2 創(chuàng)建虛擬屏幕
? 在這節(jié)中想暗,我們需要再次借助MediaProjectionManager類妇汗,調(diào)用getMediaProjection()
方法得到mMediaProjection對象備用。
? 再初始化一個ImageReader對象说莫,這個對象會在虛擬化屏幕里面用到杨箭,這個ImageReader實際上是屏幕上面的畫面。我們可以通過ImageReader.newInstance()
方法創(chuàng)建一個ImageReader對象储狭。
? 定義:ImageReader newInstance(int width, int height, int format, int maxImages)
參數(shù) | 含義 |
---|---|
int width | 寬度(此處為屏幕寬度) |
int height | 高度(此處為屏幕高度) |
int format | 圖片格式(此處為PixelFormat.RGBA_8888) |
int maxImages | 圖片的最大數(shù)量 |
? 接下來就是創(chuàng)建虛擬屏幕了互婿,可以用之前拿到的mMediaProjection,調(diào)用createVirtualDisplay
方法實現(xiàn)辽狈。定義:
createVirtualDisplay(@NonNull String name,int width, int height, int dpi, int flags, @Nullable Surface surface,@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler)
參數(shù) | 含義 |
---|---|
String name | 虛擬屏幕名字慈参,非空 |
int width | 虛擬屏幕寬度 |
int height | 虛擬屏幕高度 |
int dpi | 虛擬屏幕的DPI |
int flags | 虛擬屏幕的顯示標(biāo)志 |
Surface surface | 存放虛擬屏幕圖像的UI |
VirtualDisplay.Callback callback | 虛擬屏幕狀態(tài)發(fā)生改變的回調(diào) |
Handler handler | 上面回調(diào)所運行的線程,為null上面回調(diào)會運行在主線程里面 |
? 實現(xiàn)代碼:
private void createVirtualDisplay() {
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
"screen-mirror",
getScreenWidth(),
getScreenHeight(),
Resources.getSystem().getDisplayMetrics().densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(),
null,
null
);
}
1.3 獲取虛擬屏幕的內(nèi)容,轉(zhuǎn)為Bitmap對象
? 從存儲虛擬屏幕的ImageReader對象上,拿到里面的image圖像桃熄,這里就可以得到image的字節(jié)數(shù)組信息,再新建一個bitmap對象壮锻,將字節(jié)信息傳給bitmap,就可以拿到我們需要的圖像涮阔,這個bitmap就是我們的屏幕截圖了猜绣。需要注意的是,bitmap的色彩格式要和上面給ImageReader設(shè)置的一樣敬特。
相關(guān)代碼掰邢,這部分最好在子線程中執(zhí)行:
Image image = mImageReader.acquireLatestImage();
int width = image.getWidth();
int height = image.getHeight();
final Image.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();
1.4 代碼封裝
筆者將上述步驟封裝在ScreenShotHelper類中,代碼如下:
package com.example.testscreenshot;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
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.os.AsyncTask;
import android.os.Handler;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
public class ScreenShotHelper {
interface OnScreenShotListener {
void onFinish(Bitmap bitmap);
}
private OnScreenShotListener mOnScreenShotListener;
private ImageReader mImageReader;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private final SoftReference<Context> mRefContext;
public ScreenShotHelper(Context context, int resultCode, Intent data, OnScreenShotListener onScreenShotListener) {
this.mOnScreenShotListener = onScreenShotListener;
this.mRefContext = new SoftReference<Context>(context);
mMediaProjection = getMediaProjectionManager().getMediaProjection(resultCode, data);
mImageReader = ImageReader.newInstance(getScreenWidth(), getScreenHeight(), PixelFormat.RGBA_8888, 1);
}
public void startScreenShot() {
createVirtualDisplay();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
new CreateBitmapTask().execute();
}
}, 1000);
}
public class CreateBitmapTask extends AsyncTask<Image, Void, Bitmap> {
@Override
protected Bitmap doInBackground(Image... params) {
Image image = mImageReader.acquireLatestImage();
int width = image.getWidth();
int height = image.getHeight();
final Image.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();
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
mVirtualDisplay.release();
mMediaProjection.stop();
if (mOnScreenShotListener != null) {
mOnScreenShotListener.onFinish(bitmap);
}
}
}
private MediaProjectionManager getMediaProjectionManager() {
return (MediaProjectionManager) getContext().getSystemService(
Context.MEDIA_PROJECTION_SERVICE);
}
private void createVirtualDisplay() {
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
"screen-mirror",
getScreenWidth(),
getScreenHeight(),
Resources.getSystem().getDisplayMetrics().densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(),
null,
null
);
}
private Context getContext() {
return mRefContext.get();
}
public static int getScreenWidth() {
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
public static int getScreenHeight() {
return Resources.getSystem().getDisplayMetrics().heightPixels;
}
}
? 用法:
ScreenShotHelper screenShotHelper = new ScreenShotHelper(MainActivity.this, resultCode, data, new ScreenShotHelper.OnScreenShotListener() {
@Override
public void onFinish(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
});
screenShotHelper.startScreenShot();