最近測試了一下Android的截圖工具,最開始是這麼設計的:
public class ScreenCaptor{
public static final int REQUEST_CAPTURE = 4;
public interface OnCaptureFinishedListener {
void onCaptureSuccess(Bitmap bitmap);
void onCaptureFailure();
}
public void startCapture(int requestCode, OnCaptureFinishedListener onCaptureFinishedListener) {
//...
}
public void setOnCaptureGranted(int resultCode, Intent resultData) {
//...
}
}
// 在Activity中使用時:
public class ScreenCaptorTestActivity extends AppCompatActivity implements ScreenCaptor.OnCaptureFinishedListener {
private ScreenCaptor mScreenCaptor;
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_api_shot);
mImageView = findViewById(R.id.image_view);
mScreenCaptor = new ScreenCaptor(this);
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.start_capture:
mScreenCaptor.startCapture(REQUEST_CAPTURE,this);
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CAPTURE) {
mScreenCaptor.setOnCaptureGranted(resultCode,data);
}
}
@Override
public void onCaptureSuccess(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
@Override
public void onCaptureFailure() {
}
}
因爲截圖需要請求權限,在使用截圖工具的Activity中都要重寫onActivityResult(int,int,Intent)方法,調用ScreenCaptor的setOnCaptureGranted(int,Intent)。由此想到Fragment中也有onActivityResult方法,Fragment有不帶UI的用法。於是截圖工具可以這樣做:
public class ScreenCaptureFragment extends Fragment {
private static final String TAG = "ScreenCaptureFragment";
private static final int REQUEST_CAPTURE = 2;
public interface OnCaptureFinishedListener {
void onCaptureSuccess(Bitmap bitmap);
void onCaptureFailure();
}
private OnCaptureFinishedListener mOnCaptureFinishedListener;
private MediaProjectionManager mMediaProjectionManager;
private int mScreenWidth;
private int mScreenHeight;
private int mScreenDensity;
private ImageReader mImageReader;
private MediaProjection mMediaProjection;
private Intent mResultData;
private int mResultCode;
private VirtualDisplay mVirtualDisplay;
private ImageToBitmapAsyncTask mImageToBitmapTask;
public ScreenCaptureFragment() {
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mMediaProjectionManager = ((MediaProjectionManager) getActivity().getSystemService(Context.MEDIA_PROJECTION_SERVICE));
setupImageReader();
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private void setupImageReader() {
DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
mScreenDensity = displayMetrics.densityDpi;
mScreenWidth = displayMetrics.widthPixels;
mScreenHeight = displayMetrics.heightPixels;
// 圖片格式PixelFormat.RGBA_8888不是亂寫的,與Bitmap.Config的圖片格式對應
mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 1);
}
/**
* 啓動截屏
*
* @param onCaptureFinishedListener 截屏完成的監聽器
*/
public void startCapture(OnCaptureFinishedListener onCaptureFinishedListener) {
mOnCaptureFinishedListener = onCaptureFinishedListener;
if (mMediaProjection != null) {// 如果已經有令牌,直接拓印屏幕內容
setupVirtualDisplay();
} else if (mResultData != null && mResultCode != 0) {// 如果已經獲得錄屏權限,構建MediaProjection,然後拓印屏幕內容
setupMediaProjection();
setupVirtualDisplay();
} else {// 如果沒有錄屏權限:請求錄屏權限
requestCapturePermission(REQUEST_CAPTURE);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CAPTURE) {
if (resultCode != RESULT_OK) {
Log.e(TAG, "截屏權限被禁止!");
return;
}
mResultCode = resultCode;
mResultData = data;
setupMediaProjection();
setupVirtualDisplay();
}
}
/*獲得MediaProjection:一個允許程序截取屏幕內容的令牌*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setupMediaProjection() {
mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void requestCapturePermission(int requestCode) {
startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), requestCode);
}
/*獲得虛擬顯示,可理解爲拓印屏幕內容*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setupVirtualDisplay() {
if (mImageToBitmapTask != null && mImageToBitmapTask.isRunning()) {
Log.e(TAG,"There is already a task running");
return;
}
Image image;
do {
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCaptor",
mScreenWidth, mScreenHeight, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
image = mImageReader.acquireNextImage();
} while (image == null);
mImageToBitmapTask = new ImageToBitmapAsyncTask(mOnCaptureFinishedListener);
mImageToBitmapTask.execute(image);
}
private static class ImageToBitmapAsyncTask extends AsyncTask<Image, Void, Bitmap> {
private boolean isRunning;
private WeakReference<OnCaptureFinishedListener> mListenerWeakReference;
public ImageToBitmapAsyncTask(OnCaptureFinishedListener listener) {
mListenerWeakReference = new WeakReference<>(listener);
}
@Override
protected void onPreExecute() {
isRunning = true;
}
@Override
protected Bitmap doInBackground(Image... images) {
Image image = null;
if (images != null && images.length > 0) {
image = images[0];
}
if (image != null) {
return imageToBitmap(images[0]);
}
return null;
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private Bitmap imageToBitmap(Image image) {
if (image == null) {
return null;
}
try {
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);
return Bitmap.createBitmap(bitmap, 0, 0, width, height);
} catch (ArithmeticException e) {
return null;
} finally {
image.close();
}
}
@Override
protected void onPostExecute(Bitmap bitmap) {
isRunning = false;
OnCaptureFinishedListener listener = mListenerWeakReference.get();
if (listener == null) {
return;
}
if (bitmap != null) {
listener.onCaptureSuccess(bitmap);
} else {
listener.onCaptureFailure();
}
}
@Override
protected void onCancelled() {
isRunning = false;
}
public boolean isRunning() {
return isRunning;
}
}
@Override
public void onPause() {
super.onPause();
stopMediaProjection();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mImageToBitmapTask != null && !mImageToBitmapTask.isRunning()) {
mImageToBitmapTask.cancel(true);
mImageToBitmapTask = null;
}
releaseVirtualDisplay();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void stopMediaProjection() {
if (mMediaProjection != null) {
mMediaProjection.stop();
mMediaProjection = null;
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private void releaseVirtualDisplay() {
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.release();
mVirtualDisplay = null;
}
}
使用時不需要再重寫onActivityResult方法了:
public class ScreenCaptureActivity extends AppCompatActivity implements ScreenCaptureFragment.OnCaptureFinishedListener {
private ImageView mImageView;
protected ScreenCaptureFragment mCaptureFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_api_shot);
mImageView = findViewById(R.id.image_view);
mCaptureFragment = new ScreenCaptureFragment();
getSupportFragmentManager().beginTransaction().add(mCaptureFragment,"capture").commit();
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.start_capture:
mCaptureFragment.startCapture(this);
break;
}
}
@Override
public void onCaptureSuccess(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
@Override
public void onCaptureFailure() {
}
}