Android实现可选区域全局屏幕截屏和应用内截屏

之前文章中写过如何一行代码实现系统截屏功能:
sendBroadcast(new Intent("android.intent.action.SCREENSHOT"));

https://blog.csdn.net/HuanWen_Cheng/article/details/141254481?spm=1001.2014.3001.5501

这个方法是全屏截屏,无法进行区域截屏,android系统其实也是自带支持区域截屏的,发送截屏广播被系统接收后会执行一个hander任务:

 mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
                    mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
                    mHandler.post(mScreenshotRunnable);
   private class ScreenshotRunnable implements Runnable {
        private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
        private int mScreenshotSource = SCREENSHOT_KEY_OTHER;

        public void setScreenshotType(int screenshotType) {
            mScreenshotType = screenshotType;
        }

        public void setScreenshotSource(int screenshotSource) {
            mScreenshotSource = screenshotSource;
        }

        @Override
        public void run() {
            mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);
        }
    }

    private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable();

从这可以看出默认执行的截屏类型是TAKE_SCREENSHOT_FULLSCREEN,即全屏的,若想进行区域截屏,将类型改成TAKE_SCREENSHOT_SELECTED_REGION即可。系统源码中没有开放区域截屏广播,我们可以添加一个,在下面代码中进行修改:

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

init(){
    ...
     filter = new IntentFilter();
        filter.addAction("android.intent.action.SCREENSHOT");
        //add 
        filter.addAction("android.intent.action.SCREENSHOT_RECT");
        context.registerReceiver(mScreenshotReceiver, filter);
    ...
}



   BroadcastReceiver mScreenshotReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           //add by  支持指定区域截屏广播
            if("android.intent.action.SCREENSHOT_RECT".equals(intent.getAction())){
                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_SELECTED_REGION);
                 mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
            }else{
                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
                 mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
            }
           
            mHandler.post(mScreenshotRunnable);
        }
    };

如此修改后,即可通过发送广播android.intent.action.SCREENSHOT_RECT来触发系统区域截屏。不过这个效果是屏幕上覆盖了一层黑色半透,然后在屏幕上面任意画一个区域后就会进行今天保存。这个交互效果不太理想,选择区域是一次性的,不显示选择区域,也不能对现象区域进行拖拽或者放大缩小。

优化:自定义显示一个区域框,可以对区域框进行拖拽、放大、缩小,点击确认按钮后进行选定区域的截屏。

网上多数的截屏方案是进行应用内区域View截屏,方式如下:

        //获取指定activity顶层View
        View view = activity.getWindow().getDecorView();
        //设置View允许绘制缓存
        view.setDrawingCacheEnabled(true);
        //得到绘制缓存Bitmap
        Bitmap bitmap = view.getDrawingCache();
        //保存截图
        //saveImageToGallery(bitmap, activity);

但这个方法是无法对非本应用画面进行截图的,比如当前Activity是一个半屏弹窗,那么弹窗之外的区域是无法截取到的。所以更好的方法应是对整个屏幕进行截屏,系统中发方法是将显示设备当前一帧的数据Buffer进行截图,方法如下:

    private Bitmap captureScreenshot(Rect crop) {
        int width = crop.width();
        int height = crop.height();
        Bitmap screenshot = null;
        final Display display = getDefaultDisplay();
        final DisplayAddress address = display.getAddress();
        if (!(address instanceof DisplayAddress.Physical)) {
            Log.e(TAG, "Skipping Screenshot - Default display does not have a physical address: "
                    + display);
        } else {
            final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;

            final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(
                    physicalAddress.getPhysicalDisplayId());
            final SurfaceControl.DisplayCaptureArgs captureArgs =
                    new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
                            .setSourceCrop(crop)
                            .setSize(width, height)
                            .build();
            final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                    SurfaceControl.captureDisplay(captureArgs);
            screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
        }
        return screenshot;
    }

需要注意的是此处得到的Bitmap是硬件数据,无法直接进行编辑,需要转换一下,否则会报错:

 Bitmap targetBmp = screenshot.copy(Bitmap.Config.ARGB_8888, false);

至于自定义区域选择控件,网上有很多方案,这个提供一个样例:ScreenShotView.java

 screenShotView = new ScreenShotView(this);

        screenShotView.setColors(new int[]{0xffffff00, 0xffff0000, 0xff00ff00, 0xff0000ff});
        screenShotView.setVisibility(View.VISIBLE);

关于ScreentShotView源码样例,下一篇提供

Android应用内的弹窗通常是指Activity或Fragment内部展示的小窗口,比如Dialog、Toast或者PopupWindow。实现步骤如下: 1. **Dialog**:你可以通过`AlertDialog.Builder`创建一个自定义的对话框,显示警告、提示信息等。例如: ```java AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); dialogBuilder.setMessage("这是弹窗内容"); dialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 点击操作 } }); dialogBuilder.show(); ``` 2. **Toast**:用于显示简短的消息,一般用于临时反馈,通过`Toast.makeText()`方法创建并显示: ```java Toast.makeText(context, "这是一个提示", Toast.LENGTH_SHORT).show(); ``` 3. **PopupWindow**:如果需要更复杂的布局或者定制效果,可以使用`PopupWindow`和`PopupMenu`,它们提供了更大的灵活性。 至于全局弹窗(系统级通知),Android提供的是Notification API,它允许你在应用后台向用户发送通知,即使应用不在前台运行也能看到。创建一个Notification的例子: ```java NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID) .setContentTitle("标题") .setContentText("正文") .setSmallIcon(R.drawable.ic_notification); NotificationManagerCompat manager = NotificationManagerCompat.from(context); manager.notify(NOTIFICATION_ID, notificationBuilder.build()); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值