Android-0.Android 5.0以上使用MediaProjection截屏

源码下载

效果

权限

  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

注意,如果是小米手机,最好是动态申请权限 Android 6.0动态权限申请,或者你可以强制自行开启显示悬浮窗权限。不然你是看不到在这里插入图片描述这个悬浮窗的。

相关类获得截屏数据到ImageReader中

MediaProjectionManager

1.MediaProjectionManager是一个系统服务,可通getSystemService(Context.MEDIA_PROJECTION_SERVICE)获取。

MediaProjectionManager mediaProjectionManager = 
(MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),REQUEST_MEDIA_PROJECTION);

这里必须使用startActivityForResult 因为在createScreenCaptureIntent() 方法中会返回用户授权截取屏幕的结果,用户根据下面弹窗允许或者拒绝截屏:

用户选择后,在ActivityonActivityResult 中返回结果

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            case REQUEST_MEDIA_PROJECTION:
                if (resultCode == RESULT_OK && data != null) {
                    FloatWindowsService.setResultData(data);
                    startService(new Intent(getApplicationContext(), FloatWindowsService.class));
                }
                break;
        }
    }

如果允许,就是用FloatWindowsService来启动一个Service,注意要保存返回结果 Intent data

2.FloatWindowsService中创建一个悬浮窗mFloatView在这里插入图片描述,让mFloatView监听点击消息

        mFloatView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return mGestureDetector.onTouchEvent(event);
            }
        });

3.在点击mFloatView时触发startScreenShot

    private void startScreenShot() {
        Log.e(TAG, "startScreenShot");
        mFloatView.setVisibility(View.GONE);

        Handler handler1 = new Handler();
        handler1.postDelayed(new Runnable() {
            @Override
            public void run() {
                //获取当前屏幕内容
                startVirtual();
            }
        },5);

        handler1.postDelayed(new Runnable() {
            @Override
            public void run() {
                //生成图片保存到本地
                startCapture();
            }
        },30);

    }

MediaProjection

MediaProjection可以用来捕捉屏幕,具体来说可以截取当前屏幕和录制屏幕视频,它由MediaProjectionManager来管理和获取。

startVirtual中获取当前屏幕内容

private void startVirtual() {
        if (null != mMediaProjection){
            virtualDisplay();
        }else{
            setUpMediaProjection();// 初始化MediaProjection
            virtualDisplay();
        }
    }

setUpMediaProjection() 方法中我们获取MediaProjection 实例

    private void setUpMediaProjection() {
        if (mResultData == null){
            Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.addCategory(Intent.CATEGORY_LAUNCHER);
            startActivity(intent);
        }else{
            mMediaProjection = getMediaProjectionManager().getMediaProjection(Activity.RESULT_OK, mResultData);
        }
    }

这里传入的mResultData就是前面onActivityResult 中操作返回的结果data

最终调用virtualDisplay获得屏幕内容,并传入到mImageReader

private void virtualDisplay() {
        Log.e(TAG, "virtualDisplay");
        mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",
                mScreenWidth, mScreenHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mImageReader.getSurface(), null, null);
    }

注意这里mImageReader.getSurface()被传入,屏幕的数据也将会在mImageReaderSurface中。
另外以上对象的创建完成需要时间,可以在OnImageAvailableListener的回调方法中监听是否完成。

ImageReader

mImageReader是一个ImageReader对象,我们把它设置为RGBA_8888,和后面的Bitmap格式保持一致。

    private void createImageReader() {
        mImageReader = ImageReader.newInstance(mScreenWidth,mScreenHeight, PixelFormat.RGBA_8888,1);
    }

数据处理

mImageReader得到的屏幕内容数据转换成图片,在AsyncTask中处理,
Image.Plane中的 buffer 数据并不完全是Bitmap所需要的,需要注意下面3点

  1. Image 设置的图片格式与Bitmap设置的必须一致,这里统一设置为ARGB_8888
  2. 缓冲数据存在行间距,所以我们必须去除这些间距。
  3. Image 使用后必须调用image.close();关闭,否则再次使用会报错。
			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();

参考:
https://blog.csdn.net/adsl624153/article/details/61624286
https://blog.csdn.net/SpringIOC/article/details/78568438

猜你喜欢

转载自blog.csdn.net/hgy413/article/details/85535645