欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android实现矩形区域截屏的方法

程序员文章站 2022-03-02 23:25:02
对屏幕进行截屏并裁剪有两种方式:早截图和晚截图。早截图,就是先截取全屏,再让用户对截取到的图片进行修改;与之相对的,晚截图,就是先让用户在屏幕上划好区域,再进行截图和裁剪。...

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

晚截图可以分成三步:

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

2. 调用系统接口截屏

3. 对截图进行裁剪

效果图如下:

Android实现矩形区域截屏的方法

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

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

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,截屏如下图所示:

Android实现矩形区域截屏的方法

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

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);

处理后的截图如下:

Android实现矩形区域截屏的方法

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

完整代码可以参考bigbang项目的marksizeview类、screencaptureactivity类和screencapture类。