当前位置: 首页 > 科技观察

如何在Android上截取矩形区域的截图

时间:2023-03-16 12:38:52 科技观察

截屏和裁剪有两种方式:早截图和晚截图。早截图是指先截取全屏,然后让用户修改截取的图片;相反,延迟截图意味着让用户在截图和裁剪之前在屏幕上绘制一个好的区域。事实上,两者之间并没有太大的区别。这篇文章会讲到如何实现后期截图。后期截图可以分为三个步骤:在屏幕上标记截图的矩形区域调用系统界面进行截图并裁剪截图如下:矩形区域的截图第一步:在屏幕上识别截图区域屏幕首先确定需要识别的截图区域功能:拖动手指形成一个矩形区域;可以拖动绘制的矩形区域进行移动;可以拖动矩形区域的边框来调整大小;选择完成后,有“确认”和“取消”功能,可以得到选中区域的位置。需要注意的是按钮的位置要自适应。例如,如果跑马灯几乎占据了整个屏幕,那么按钮应该放在跑马灯内部。最简单的方法就是写一个自定义的View,根据触摸的位置执行不同的功能。实现很简单,只要仔细控制每个状态即可,代码请参考Bigbang项目的MarkSizeView类。第二步调用系统界面进行截图。截图必须在Activity中进行,因为需要调用startActivityForResult()。不过mMediaProjectionManager也可以传递给service进行后续处理。还需要注意的是,截图时Activity本身应该是透明的,不能影响截取的内容。直接看代码:publicclassScreenCaptureActivityextendsActivity{privatestaticfinalStringTAG=ScreenCaptureActivity.class.getName();privateMediaProjectionManagermMediaProjectionManager;privateintREQUEST_MEDIA_PROJECTION=1;privateSimpleDateFormatdateFormat;privateStringpathImage;privateWindowManagermWindowManager;privateImageReadermImageReader;privateMediaProjectionmMediaProjection;privateintmResultCode;privateIntentmResultData;privateVirtualDisplaymVirtualDisplay;privateStringstrDate;privateintwindowWidth;privateintwindowHeight;privateStringnameImage;privateintmScreenDensity;@RequiresApi(api=Build.VERSION_CODES.LOLLIPOP)@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);mMediaProjectionManager=(MediaProjectionManager)getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);createVirtualEnvironment();startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(),REQUEST_MEDIA_PROJECTION);}@TargetApi(Build.VERSION_CODES.LOLLIPOP)@OverridepublicvoidonActivityResult(intrequestCode,intresultCode,Intentdata){if(requestCode==REQUEST_MEDIA_PROJECTION){if(resultCode!=Activity.RESULT_OK){return;}elseif(data!=null&&resultCode!=0){mResultCode=resultCode;mResultData=data;startVirtual();newHandler(Looper.getMainLooper()).postDelayed(newRunnable(){@Overridepublicvoidrun(){startCapture();}},100);}}}@RequiresApi(api=Build.VERSION_CODES.KITKAT)privatevoidcreateVirtualEnvironment(){dateFormat=newSimpleDateFormat("yyyy_MM_dd_hh_mm_ss");strDate=dateFormat.format(newDate());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();DisplayMetricsmetrics=newDisplayMetrics();mWindowManager.getDefaultDisplay().getMetrics(metrics);mScreenDensity=metrics.densityDpi;mImageReader=ImageReader.newInstance(windowWidth,windowHeight,0x1,2);//ImageFormat.RGB_565Log.i(TAG,"preparedthevirtualenvironment");}@TargetApi(Build.VERSION_CODES.LOLLIPOP)publicvoidstartVirtual(){if(mMediaProjection!=null){Log.i(TAG,"wanttodisplayvirtual");virtualDisplay();}else{Log.i(TAG,"startscreencaptureintent");Log.i(TAG,"wanttobuildmediaprojectionanddisplayvirtual");setUpMediaProjection();virtualDisplay();}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)publicvoidsetUpMediaProjection(){mMediaProjection=mMediaProjectionManager.getMediaProjection(mResultCode,mResultData);Log.i(TAG,"mMediaProjectiondefined");}@TargetApi(Build.VERSION_CODES.LOLLIPOP)privatevoidvirtualDisplay(){mVirtualDisplay=mMediaProjection.createVirtualDisplay("screen-mirror",windowWidth,windowHeight,mScreenDensity,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mImageReader.getSurface(),null,null);Log.i(TAG,"virtualdisplayed");}@TargetApi(Build.VERSION_CODES.LOLLIPOP)privatevoidstartCapture(){strDate=dateFormat.format(newjava.util.Date());nameImage=pathImage+strDate+".png";Imageimage=mImageReader.acquireLatestImage();intwidth=image.getWidth();intheight=image.getHeight();finalImage.Plane[]planes=image.getPlanes();finalByteBufferbuffer=planes[0].getBuffer();intpixelStride=planes[0].getPixelStride();introwStride=planes[0].getRowStride();introwPadding=rowStride-pixelStride*width;Bitmapbitmap=Bitmap.createBitmap(width+rowPadding/pixelStride,height,Bitmap.Config.ARGB_8888);bitmap.copyPixelsFromBuffer(buffer);bitmap=Bitmap.createBitmap(位图,0,0,宽度,height);image.close();Log.i(TAG,"imagedatacaptured");//保存截图结果,如果需要裁剪图片,在这里处理bitmapif(bitmap!=null){try{FilefileImage=newFile(nameImage);if(!fileImage.exists()){fileImage.createNewFile();Log.i(TAG,"imagefilecreated");}FileOutputStreamout=newFileOutputStream(fileImage);if(out!=null){bitmap.compress(Bitmap.CompressFormat.PNG,100,out);out.flush();out.close();Intentmedia=newIntent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);UricontentUri=Uri。fromFile(fileImage);media.setData(contentUri);this.sendBroadcast(media);Log.i(TAG,"screenimagesaved");}}catch(FileNotFoundExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)privatevoidtearDownMediaProjection(){if(mMediaProjection!=null){mMediaProjection.stop();mMediaProjection=null;}Log.i(TAG,"mMediaProjectionundefined");}}第三步是裁剪屏幕截图。根据第一步得到的截图区域mRect,对第二步得到的截图结果的位图进行裁剪: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;intcut_width=Math.abs(mRect.left-mRect.right);intcut_height=Math.abs(mRect.top-mRect.bottom);if(cut_width>0&&cut_height>0){BitmapcutBitmap=Bitmap.createBitmap(bitmap,mRect.left,mRect.top,cut_width,cut_height);}需要注意的是,调用系统截图函数时,如果手机有NavigationBar(虚拟导航栏),windowHeight的值不包括NavigationBar的高度是的,如果不做调整,截图会被压缩。如何获取屏幕真实高度,可以参考Android如何判断NavigationBar是否显示(获取屏幕真实高度)。而且NavigationBar也会导致截图的结果出现边框,而且边框的颜色是透明的,因为第二步代码中的rowPadding!=0,截图如下图:结果使用带NavigationBar的系统截图,那么如果我们要保存或者裁剪截图的结果,就必须去掉frame才能找出真正的内容区域,也就是第一个不透明像素和最后一个不透明像素之间的内容不透明像素,然后可以对得到的区域进行编辑第三步裁剪,代码如下:int[]pixel=newint[width];bitmap.getPixels(pixel,0,width,0,0,width,1);intleftPadding=0;intrightPadding=width;for(inti=0;i=0;i--){if(pixel[i]!=0){rightPadding=i;break;}}bitmap=Bitmap.createBitmap(bitmap,leftPadding,0,rightPadding-leftPadding,height);处理后的截图如下:获取截图结果的内容部分你可能会想,既然rowPadding!=0导致边框出现,而且边框只在右边,那为什么不直接截掉rowPadding宽度的内容在右侧?其实如果不调整windowHeight,左边也会生成一个frame,所以就用了上面的方法。完整代码请参考Bigbang项目的MarkSizeView类、ScreenCaptureActivity类和ScreenCapture类。