Android中实现截图的几种方式

来源:互联网 发布:深信服网络行为管理 编辑:程序博客网 时间:2024/06/05 00:21

一、起始原因


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


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

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

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

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


二、具体实现方式


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 <Params, Progress, Result> void executeParallel(AsyncTask<Params, Progress, Result> 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<Image, Void, Bitmap> {        @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();



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





三、事后总结


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

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的质量,大家依据自己的需求来做变更~~~

反思二:

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

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



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

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


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


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




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

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


0 0
原创粉丝点击