Android中实现截图的几种方式

一、起始原因


最近项目需求中需要实现屏幕截图,开启了新一轮的翻腾,找寻。是的,我就是一个搬运工,简单的搬运工~~做不完的功能,连接不断地需求~~


基本需求:实现当前页面截图并保存;

扩展需求:截图去除自己添加的控件;

完善需求:截图响应速度要快;

反馈完善需求:适配所有机型。


二、具体实现方式


1),第一种实现方式

    /**
     * 对View进行量测,布局后截图
     *
     * @param view
     * @return
     */
    public Bitmap convertViewToBitmap(View view) {
        view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        return bitmap;
    }

此种实现方式,基本可以实现所有View的截图及全屏幕的截图。

在实际的需求中,是对WebView进行截图,且WebView的展示中,是使用绘制程序绘制部分内容。这种方式,绘制的内容截屏展示不出来,只能另寻他法。


2)第二种实现方式
    /**
     * 获取整个窗口的截图
     *
     * @param context
     * @return
     */
    @SuppressLint("NewApi")
    private Bitmap captureScreen(Activity context) {
        View cv = context.getWindow().getDecorView();

        cv.setDrawingCacheEnabled(true);
        cv.buildDrawingCache();
        Bitmap bmp = cv.getDrawingCache();
        if (bmp == null) {
            return null;
        }

        bmp.setHasAlpha(false);
        bmp.prepareToDraw();
        return bmp;
    }


3),第三种实现方式【实质是将view作为原图绘制出来】

    /**
     * 对单独某个View进行截图
     *
     * @param v
     * @return
     */
    private Bitmap loadBitmapFromView(View v) {
        if (v == null) {
            return null;
        }
        Bitmap screenshot;
        screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.RGB_565);
        Canvas c = new Canvas(screenshot);
        c.translate(-v.getScrollX(), -v.getScrollY());
        v.draw(c);
        return screenshot;
    }


4),第四种实现方式【针对WebView的实现方式】

  /**
     * 对WebView进行截图
     *
     * @param webView
     * @return
     */
    public static Bitmap captureWebView1(WebView webView) {//可执行
        Picture snapShot = webView.capturePicture();
        Bitmap bmp = Bitmap.createBitmap(snapShot.getWidth(),
                snapShot.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        snapShot.draw(canvas);
        return bmp;
    }

5),第五种实现方式

此种实现方式相对较复杂,且很多方法不支持版本较低的Android系统版本。但能够很好地处理WebView绘制内容截图不成功的问题。

在实际的执行中,通过版本和Android内置系统的判断,多种截图方法综合使用,能够实现产品的需求。


只是后来,因为产品业务调整,这一块功能隐藏不使用了~~让我哭会~~~不管怎么样,也算是学东西了,只是后面的路,需要走的更稳妥,更踏实一些~~~

//定义使用变量

    /**
     * 截屏相关
     */
    private MediaProjectionManager mediaProjectionManager;
    private MediaProjection mMediaProjection;
    private VirtualDisplay mVirtualDisplay;
    private static Intent mResultData = null;
    private ImageReader mImageReader;
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mLayoutParams;
    private GestureDetector mGestureDetector;
    private ImageView mFloatView;
    private int mScreenWidth;
    private int mScreenHeight;
    private int mScreenDensity;
    private String mPhoneType;
    public static final int REQUEST_MEDIA_PROJECTION = 18;
//onCreate()初始化变量

       /**
         * 初始化变量
         */
        mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);

        mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        mWindowManager.getDefaultDisplay().getMetrics(metrics);
        mScreenDensity = metrics.densityDpi;
        mScreenWidth = metrics.widthPixels;
        mScreenHeight = metrics.heightPixels;
        mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 1);
//截图方法体的实现

  @Override
    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) {
                    mResultData = data;
                    //startService(new Intent(getApplicationContext(), FloatWindowsService.class));
                }
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            stopVirtual();
            tearDownMediaProjection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void startScreenShot() {
        Handler handler1 = new Handler();
        handler1.postDelayed(new Runnable() {
            public void run() {
                // start virtual
                startVirtual();
            }
        }, 5);

        handler1.postDelayed(new Runnable() {
            public void run() {
                // capture the screen
                startCapture();

            }
        }, 30);
    }

    public void startVirtual() {
        if (mMediaProjection != null) {
            virtualDisplay();
        } else {
            setUpMediaProjection();
            virtualDisplay();
        }
    }

    private void stopVirtual() {
        if (mVirtualDisplay == null) {
            return;
        }
        mVirtualDisplay.release();
        mVirtualDisplay = null;
    }

    private void startCapture() {

        Image image = mImageReader.acquireLatestImage();

        if (image == null) {
            startScreenShot();
        } else {
            SaveTask mSaveTask = new SaveTask();
            // mSaveTask.execute(image);
            if (Build.VERSION.SDK_INT >= 11) {
                // From API 11 onwards, we need to manually select the
                // THREAD_POOL_EXECUTOR
                AsyncTaskCompatHoneycomb.executeParallel(mSaveTask, image);
            } else {
                // Before API 11, all tasks were run in parallel
                mSaveTask.execute(image);
            }
            // AsyncTaskCompat.executeParallel(mSaveTask, image);
        }
    }

    static class AsyncTaskCompatHoneycomb {

        static  void executeParallel(AsyncTask task, Params... params) {
            // 这里显示调用了THREAD_POOL_EXECUTOR,所以就可以使用该线程池了
            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
        }

    }

    @SuppressLint("NewApi")
    private void virtualDisplay() {
        Surface sf = mImageReader.getSurface();
        mVirtualDisplay = mMediaProjection.createVirtualDisplay(
                "screen-mirror", mScreenWidth, mScreenHeight, mScreenDensity,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mImageReader.getSurface(), null, null);
    }

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

    private MediaProjectionManager getMediaProjectionManager() {
        return (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    }

    public class SaveTask extends AsyncTask {

        @Override
        protected Bitmap doInBackground(Image... params) {
            if (params == null || params.length < 1 || params[0] == null) {
                return null;
            }

            Image image = params[0];

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

//			File file = new File(SAVE_REAL_PATH);
//			if (bitmap != null) {
//				try {
//                    if (!file.exists()) {
//                        file.mkdirs();
//                    }
//                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.US);
//                    String fileImage = file.getAbsolutePath() + "/" + sdf.format(new Date()) + ".jpg";
//					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(file);
//						media.setData(contentUri);
//						sendBroadcast(media);
//						fileDestUri = fileImage;
//					}
//				} catch (FileNotFoundException e) {
//					e.printStackTrace();
//					file = null;
//				} catch (IOException e) {
//					e.printStackTrace();
//					file = null;
//				}
//			}

//			if (file != null) {
            return bitmap;
//			}
//			return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            // 预览图片
            if (bitmap != null) {
                //也可以处理保存图片逻辑
                captureIV.setImageBitmap(bitmap);
            }
        }
    }

    private void tearDownMediaProjection() {
        if (mMediaProjection != null) {
            mMediaProjection.stop();
            mMediaProjection = null;
        }
    }

//使用

                startScreenShot();



好吧,展示效果,兄弟们知道大概什么情形~~

Android中实现截图的几种方式_第1张图片

Android中实现截图的几种方式_第2张图片

三、事后总结


除去上面展示的形式,还测试了其他几种方式,并没有获取到想要的结果。要是兄弟们知道怎么解决,可以一起沟通,一起交流哈~~

1),第一种尝试

    /**
     * 应用反射的方法
     * 【反射不成功】
     *
     * @return
     */
    private Bitmap getBitmapReverse() {
        Bitmap mScreenBitmap = null;
        DisplayMetrics mDisplayMetrics = new DisplayMetrics();
        float[] dims = {mDisplayMetrics.widthPixels,
                mDisplayMetrics.heightPixels};

        Class demo = null;
        try {
            demo = Class.forName("android.view.Surface");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            Method method = demo.getMethod("screenshot", new Class[]{int.class, int.class});
            mScreenBitmap = (Bitmap) method.invoke(demo.newInstance(), (int) dims[0], (int) dims[1]);
            //这里其实可以直接用null替换demo.newInstance(),因为screenshot是静态方法,所以第一个invoke的第一个参数会被自动忽略~所以其实你填什么都没关系。
            //获取的返回值是个bitmap,然后我们就可以为所欲为了~
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mScreenBitmap;
    }
应用反射的方法,反射Surface成功,但是反射screenshot()方法不成功,没能够实现截图;


2),第二种尝试

    /**
     * 需要root权限
     *
     * @param activity
     * @return
     */
    public Bitmap captureScreenSystem(Activity activity) {
        // 获取屏幕大小:
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager WM = (WindowManager) activity
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = WM.getDefaultDisplay();
        display.getMetrics(metrics);
        int height = metrics.heightPixels; // 屏幕高
        int width = metrics.widthPixels; // 屏幕的宽
        // 获取显示方式
        int pixelformat = display.getPixelFormat();
        PixelFormat localPixelFormat1 = new PixelFormat();
        PixelFormat.getPixelFormatInfo(pixelformat, localPixelFormat1);
        int deepth = localPixelFormat1.bytesPerPixel;// 位深
        byte[] piex = new byte[height * width * deepth];
        try {
            Runtime.getRuntime().exec(
                    new String[]{"/system/bin/su", "-c",
                            "chmod 777 /dev/graphics/fb0"});
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            // 获取fb0数据输入流
            InputStream stream = new FileInputStream(new File(
                    "/dev/graphics/fb0"));
            DataInputStream dStream = new DataInputStream(stream);
            dStream.readFully(piex);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 保存图片
        int[] colors = new int[height * width];
        for (int m = 0; m < colors.length; m++) {
            int r = (piex[m * 4] & 0xFF);
            int g = (piex[m * 4 + 1] & 0xFF);
            int b = (piex[m * 4 + 2] & 0xFF);
            int a = (piex[m * 4 + 3] & 0xFF);
            colors[m] = (a << 24) + (r << 16) + (g << 8) + b;

        }
        // piex生成Bitmap
        Bitmap bitmap = Bitmap.createBitmap(colors, width, height,

                Bitmap.Config.ARGB_8888);
        return bitmap;
    }

需要root权限,客户当然不给了~~~~

反思一:

在实现图片的保存中,保存时耗时操作,为更好地用户体验,最好保存图片在子线程进行。

以下是压缩生成图片的方法:

       FileOutputStream out = null;
                        try {
                            out = new FileOutputStream(fname);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                        bitmap1.compress(Bitmap.CompressFormat.JPEG, 20, out);
选择图片的格式,压缩图片质量,都会影响生成图片的快慢。在实际中,项目保存图片应用20的质量,大家依据自己的需求来做变更~~~

反思二:

产品决定了上层了流程,会影响后续的很多环节。产品一旦修改,研发,测试都得跟着变更~时间延期,工作重复,都是影响团队决战胜利的因素。

当然了,作为研发,实现产品的需求是我们的天职。别给产品讨论需求哦~~

Android中实现截图的几种方式_第3张图片


哈哈哈,若是倾权,立删~~~

研发也是一个小人物,调侃调侃生活~~~做好自己该做的,把自己能做的做得更好,愿天下和平~~~~


欢迎小伙伴共同提高进步~~~~


异次元传送门   ----  哈哈,我就是一个Demo而已~_~




分离也许是为了更好的团聚,闯荡也许是为了更好的生活……又有谁想背井离乡孤独一人拼搏了,又有谁不想花前月下团团圆圆啦……生活是现实的,人都活在现实生活之中,每一个人,每一个家庭都有各自的活法及生活方式,谁不希望美好的生活,美好的生活是要努力打拼奋斗获得的……

趁还有时间,给自已一点不一样~~~~


你可能感兴趣的:(Android进阶等级一,Android截图,ScreenCapture)