视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Android实现矩形区域截屏的方法
2020-11-09 07:00:54 责编:小采
文档


对屏幕进行截屏并裁剪有两种方式:早截图和晚截图。早截图,就是先截取全屏,再让用户对截取到的图片进行修改;与之相对的,晚截图,就是先让用户在屏幕上划好区域,再进行截图和裁剪。其实两者并没有什么太大的区别,这篇就说说怎么实现晚截图。

晚截图可以分成三步:

1. 在屏幕上标出截图的矩形区域

2. 调用系统接口截屏

3. 对截图进行裁剪

效果图如下:

第一步、在屏幕上标识出截图区域

首先确定标识截图区域所需要的功能:

1. 手指拖动形成矩形区域;

2. 可以拖动已经划好的矩形区域进行移动;

3. 可以拖动矩形区域的边框调整大小;

4. 选择完成以后,有“确认”和“取消”功能,“确认”时可以获得选取的区域位置。需要注意的是,按钮的位置应该能够自适应,比如选框几乎占据全屏的情况下,应该把按钮放到选框内部。

最简单的方式就是写一个自定义View,根据touch的位置执行不同的功能即可。实现很简单,只要细心把每一种状态就行,代码请看Bigbang项目的MarkSizeView类。

第二步、调用系统接口截屏

截屏必须在Activity中进行,因为需要调用startActivityForResult()。不过也可以把mMediaProjectionManager传到service中进行后续处理。

还要注意的是Activity本身在截屏的时候应该是透明的,不能对要截取得内容有影响。

直接看代码:

public class ScreenCaptureActivity extends Activity {
 private static final String TAG = ScreenCaptureActivity.class.getName();
 private MediaProjectionManager mMediaProjectionManager;
 private int REQUEST_MEDIA_PROJECTION = 1;
 private SimpleDateFormat dateFormat;
 private String pathImage;
 private WindowManager mWindowManager;
 private ImageReader mImageReader;
 private MediaProjection mMediaProjection;
 private int mResultCode;
 private Intent mResultData;
 private VirtualDisplay mVirtualDisplay;
 private String strDate;
 private int windowWidth;
 private int windowHeight;
 private String nameImage;
 private int mScreenDensity;
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 mMediaProjectionManager = (MediaProjectionManager) getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
 createVirtualEnvironment();
 startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
 }
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data) {
 if (requestCode == REQUEST_MEDIA_PROJECTION) {
 if (resultCode != Activity.RESULT_OK) {
 return;
 } else if (data != null && resultCode != 0) {
 mResultCode = resultCode;
 mResultData = data;
 startVirtual();
 new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
 @Override
 public void run() {
 startCapture();
 }
 },100);
 }
 }
 }
 @RequiresApi(api = Build.VERSION_CODES.KITKAT)
 private void createVirtualEnvironment() {
 dateFormat = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss");
 strDate = dateFormat.format(new Date());
 pathImage = Environment.getExternalStorageDirectory().getPath() + "/Pictures/";
 nameImage = pathImage + strDate + ".png";
 mMediaProjectionManager = (MediaProjectionManager) getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
 mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
 windowWidth = mWindowManager.getDefaultDisplay().getWidth();
 windowHeight = mWindowManager.getDefaultDisplay().getHeight();
 DisplayMetrics metrics = new DisplayMetrics();
 mWindowManager.getDefaultDisplay().getMetrics(metrics);
 mScreenDensity = metrics.densityDpi;
 mImageReader = ImageReader.newInstance(windowWidth, windowHeight, 0x1, 2); //ImageFormat.RGB_565
 Log.i(TAG, "prepared the virtual environment");
 }
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public void startVirtual() {
 if (mMediaProjection != null) {
 Log.i(TAG, "want to display virtual");
 virtualDisplay();
 } else {
 Log.i(TAG, "start screen capture intent");
 Log.i(TAG, "want to build mediaprojection and display virtual");
 setUpMediaProjection();
 virtualDisplay();
 }
 }
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public void setUpMediaProjection() {
 mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
 Log.i(TAG, "mMediaProjection defined");
 }
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 private void virtualDisplay() {
 mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",
 windowWidth, windowHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
 mImageReader.getSurface(), null, null);
 Log.i(TAG, "virtual displayed");
 }
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 private void startCapture() {
 strDate = dateFormat.format(new java.util.Date());
 nameImage = pathImage + strDate + ".png";
 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();
 Log.i(TAG, "image data captured");
 //保存截屏结果,如果要裁剪图片,在这里处理bitmap
 if (bitmap != null) {
 try {
 File fileImage = new File(nameImage);
 if (!fileImage.exists()) {
 fileImage.createNewFile();
 Log.i(TAG, "image file created");
 }
 FileOutputStream out = new FileOutputStream(fileImage);
 if (out != null) {
 bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
 out.flush();
 out.close();
 Intent media = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
 Uri contentUri = Uri.fromFile(fileImage);
 media.setData(contentUri);
 this.sendBroadcast(media);
 Log.i(TAG, "screen image saved");
 }
 } catch (FileNotFoundException e) {
 e.printStackTrace();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 private void tearDownMediaProjection() {
 if (mMediaProjection != null) {
 mMediaProjection.stop();
 mMediaProjection = null;
 }
 Log.i(TAG, "mMediaProjection undefined");
 }
}

第三步、对截图进行裁剪

根据第一步得到的截图区域mRect对第二步中得到的截屏结果bitmap进行裁剪:

if (mRect != null) {
 if (mRect.left < 0)
 mRect.left = 0;
 if (mRect.right < 0)
 mRect.right = 0;
 if (mRect.top < 0)
 mRect.top = 0;
 if (mRect.bottom < 0)
 mRect.bottom = 0;
 int cut_width = Math.abs(mRect.left - mRect.right);
 int cut_height = Math.abs(mRect.top - mRect.bottom);
 if (cut_width > 0 && cut_height > 0) {
 Bitmap cutBitmap = Bitmap.createBitmap(bitmap, mRect.left, mRect.top, cut_width, cut_height);
}

需要注意的是,在调用系统截屏功能的时候,如果手机有NavigationBar(虚拟导航栏),windowHeight的取值就是不包括NavigationBar的高度的,如果不进行调整,就会导致截屏被压缩。如何获取屏幕的真实高度,可以参考Android如何判断NavigationBar是否显示(获取屏幕真实的高度)。

而且NavigationBar还会导致截屏的结果出现边框,边框的颜色是透明的,原因是第二步代码中的rowPadding!=0,截屏如下图所示:

那么如果我们想要对截图的结果进行保存或者裁剪,就必须要去除边框,找出真正的内容区域,也就是在第一个不透明的像素和最后一个不透明像素之间的内容,然后才能对得到的区域进行第三步的裁剪,代码如下:

int[] pixel=new int[width];
bitmap.getPixels(pixel,0,width ,0,0,width,1);
int leftPadding=0;
int rightPadding=width;
for (int i=0;i<pixel.length;i++){
 if (pixel[i]!=0){
 leftPadding=i;
 break;
 }
}
for (int i=pixel.length-1;i>=0;i--){
 if (pixel[i]!=0){
 rightPadding=i;
 break;
 }
}
bitmap=Bitmap.createBitmap(bitmap,leftPadding, 0, rightPadding-leftPadding, height);

处理后的截图如下:

你可能会觉得既然是rowPadding!=0导致出现边框,而且边框只在右边,为什么不直接把右边rowPadding宽度的内容截掉呢?其实是因为如果不调整windowHeight,就会在左边也产生框,所以才用了上面的方法。

完整代码可以参考Bigbang项目的MarkSizeView类、ScreenCaptureActivity类和ScreenCapture类。

您可能感兴趣的文章:

  • Android截屏方案实现原理解析
  • Android截屏分享功能
  • Android 下调试手机截屏的方法
  • Android 实现截屏功能的实例
  • android长截屏原理及实现代码
  • Android实现截屏方式整理(总结)
  • Android截屏SurfaceView黑屏问题的解决办法
  • Android截屏截图的几种方法总结
  • android中Webview实现截屏三种方式小结
  • Android 获取浏览器当前分享页面的截屏示例
  • Android Activity 不能被截屏的解决方法
  • 下载本文
    显示全文
    专题