Android 截图和录屏

作者: 秀花123 | 来源:发表于2018-07-09 12:08 被阅读242次

    一、Android 截图

    Android 截图在这里分为三类:

    • 截取除状态栏的屏幕
    • 截取某个控件或区域
    • 使用 MediaProjection 截图

    1.截取除状态栏的屏幕

    该方式是使用 View 的 Cache 机制生成 View 的图像缓存保存为 Bitmap。
    主要的 API 如下:

    • void setDrawingCacheEnabled(boolean flag) 开启或关闭 View 的 Cache,设置为 false 后,系统也会自动把原来的 Cache 销毁。
    • void buildDrawingCache() 创建 Cache,可不调用
    • Bitmap getDrawingCache() 获取 View 的 Cache 图片
    • void destroyDrawingCache()销毁 Cache 。若想更新 Cache,必须要调用该方法把旧的 Cache 销毁,才能建立新的。

    示例代码

            View dView = getWindow().getDecorView();
            dView.setDrawingCacheEnabled(true);
            dView.buildDrawingCache();
            Bitmap bitmap = Bitmap.createBitmap(dView.getDrawingCache());
            if(bitmap != null){
                imageView.setImageBitmap(bitmap);
            }
            dView.setDrawingCacheEnabled(false);
    

    2.截取某个控件或区域

    该方式的原理和上面一样,都是利用 View 的 Cache 机制,不同点在于,这里的 View 不是 DecorView
    示例代码:

            View view = ivSrc;
            view.setDrawingCacheEnabled(true);
            view.buildDrawingCache();
            Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
            if (bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            view.setDrawingCacheEnabled(false);
    

    还有一种方式是将 View 绘制到 Canvas

            View view = ivSrc;
            //根据 View 的宽高创建 Bitmap 对象
            Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),view.getHeight(), Bitmap.Config.ARGB_8888);
            //将以上创建的 Bitmap 指定为要绘制的 Bitmap 作为参数创建画布
            Canvas canvas = new Canvas(bitmap);
            //将 View 绘制在画布上
            view.draw(canvas);
            imageView.setImageBitmap(bitmap);
    

    ListView、ScrollView、WebView、RecyclerView 截长图都可以用使用此方法

    3、使用 MediaProjection

    ​ Android 在5.0 之后支持了实时录屏的功能。通过实时录屏我们可以拿到截屏的图像。
    大体步骤如下:

    1. 初始化一个 MediaProjectionManager 对象
    MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    
    1. 创建并启动 Intent
               Intent captureIntent = mediaProjectionManager.createScreenCaptureIntent();
               startActivityForResult(captureIntent, RECORD_REQUEST_CODE);
    
    1. 在 Activity 的 onActivityResult 方法中获取 'MediaProjection' 对象
    MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
    
    1. 创建 ImageReader 对象
    //参数1:默认图像的宽度像素
    //参数2:默认图像的高度像素
    //参数3:图像的像素格式
    //参数4:用户想要读图像的最大数量
    ImageReader imageReader = ImageReader.newInstance(
    metrics.widthPixels, metrics.heightPixels,PixelFormat.RGBA_8888, 1);
    

    ImageReader 类允许应用程序直接访问呈现表面的图像数据创建 ImageReader 对象
    主要操作:

    • getSurface()//得到一个表面,可用于生产这个 ImageReader 的图像
    • acquireLatestImage() //从ImageReader的队列获得最新的图像,放弃旧的图像。
    • acquireNextImage() //从ImageReader的队列获取下一个图像
    • getMaxImages()//最大数量的图像
    • getWidth() //每个图像的宽度,以像素为单位。
    • getHeight() //每个图像的高度,以像素为单位。
    • getImageFormat()//图像格式
    • close() //释放与此ImageReader相关的所有资源。用完记得关
    • setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler)//注册一个监听器,当ImageReader有一个新的Image变得可用时候调用。
    1. 通过 MediaProjection 创建 VirtualDisplay 对象,把内容渲染给 ImageRaederSurface 控件
    mediaProjection.createVirtualDisplay("Capture", 
    metrics.widthPixels, metrics.heightPixels, 2,
         DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                        imageReader.getSurface(), null, null);
    

    VirtualDisplay 类代表一个虚拟显示器,调用 createVirtualDisplay() 方法,将虚拟显示器的内容渲染在一个 Surface 控件上,当进程终止时虚拟显示器会被自动的释放,并且所有的 Window 都会被强制移除。当不再使用他时,你应该调用 release() 方法来释放资源。

    1. 通过 ImageReader 获取 Image 生成 Bitmap
    new Thread(){
                    @Override
                    public void run() {
                        while (true) {
                            Image image = imageReader.acquireNextImage();
                            //8、
                            if (image != null) {
                                Image.Plane[] plane = image.getPlanes();
                                ByteBuffer buffers = plane[0].getBuffer();
                                int pixelStride = plane[0].getPixelStride();
                                int rowStride = plane[0].getRowStride();
                                int rowPadding = rowStride - pixelStride * metrics.widthPixels;
    
                                Bitmap bitmap = Bitmap.createBitmap(metrics.widthPixels + rowPadding
                                                / pixelStride, metrics.heightPixels,
                                        Bitmap.Config.ARGB_8888);
                                bitmap.copyPixelsFromBuffer(buffers);
                                FileUtils.saveBitmap(bitmap);
                                image.close();
                            }
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }.start();
    

    Image 为图片数据,Plane 为 Image 的抽象内部类,
    image.getPlanes() 获取该图片的像素矩阵,返回值为一个 Plane[] 矩阵
    plane.getBuffer() 获取图像数据,getPixelStride()getRowStride() 为获取 Iamge 的一些跨距,经过一系列转换得到图像的尺寸,创建 Bitmap 对象,然后从 Image 的 ByteBuffer 中拷贝像素数据生成 Bitmap

    二、MediaProjection 录屏

    录屏的实现需要使用 MediaRecoder 类
    前面几步同截图类似

    1. 初始化一个 MediaProjectionManager 对象
    2. 创建并启动 Intent
    3. 在 Activity 的 onActivityResult 方法中获取 MediaProjection 对象
    4. 初始化 MediaRecorder并准备录制
    private void initRecorder() {
            mediaRecorder = new MediaRecorder();
            width = displayMetrics.widthPixels;
            height = displayMetrics.heightPixels;
            dpi = displayMetrics.densityDpi;
          // 视频最大的尺寸 720 * 1280 ,其他视频尺寸使用屏幕大小
            if (dpi > DisplayMetrics.DENSITY_XHIGH) {
                width = (orientation == Configuration.ORIENTATION_LANDSCAPE ? 1280 : 720);
                height = (orientation == Configuration.ORIENTATION_LANDSCAPE ? 720 : 1280);
            }
            //如果是横屏,视频输出时旋转90度
            mediaRecorder.setOrientationHint(orientation != Configuration.ORIENTATION_LANDSCAPE ? 0 : 90);
             //  音频源,这里需要 android.permission.RECORD_AUDIO 权限
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); 
            //  视频来源  
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); 
            //  视频输出格式        
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            // 录制输出文件
             currentVideoFilePath = getRecorderDir();
            mediaRecorder.setOutputFile(currentVideoFilePath);
            //视频编码格式
            mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            //音频编码格式
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            // 设置最大时长5分钟
            mediaRecorder.setMaxDuration(1 * 60 * 1000);        
            //  设置视频文件的比特率,经过测试该属性对于视频大小影响最大  
            mediaRecorder.setVideoEncodingBitRate(1 * 1024 * 1024);  
            //设置视频分辨率
            mediaRecorder.setVideoSize(width, height);
            //设置视频帧频率
            mediaRecorder.setVideoFrameRate(30);
    
            // 录制发生错误的监听
            mediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
                @Override
                public void onError(MediaRecorder mr, int what, int extra) {
    
                }
            }); 
           //记录录制时出现的信息事件
            mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
                @Override
                public void onInfo(MediaRecorder mr, int what, int extra) {
    
                }
            });
            try {
            //准备录制
                mediaRecorder.prepare();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
    5. 创建 VirtualDisplay 以进行录屏
    virtualDisplay = mediaProjection.createVirtualDisplay("MainScreen", width, height, dpi,
    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder.getSurface(), null, null);
    

    VirtualDisplay 的渲染目标 Surface 设置为 MediaRecordergetSurface,后面我就可以通过 MediaRecorder 将屏幕内容录制下来

    6、开始录制
    mediaRecorder.start();
    
    7、停止录制
    mediaRecorder.stop();
    mediaRecorder.reset();
    

    相关文章

      网友评论

        本文标题:Android 截图和录屏

        本文链接:https://www.haomeiwen.com/subject/yuejuftx.html