当前位置:   article > 正文

android 各种View截图总结_android view 截图

android view 截图

普通View截图

image

获取View截图

 

  1. /**
  2. * 获取控件截图(黑色背景)
  3. *
  4. * @param view view
  5. * @return Bitmap
  6. */
  7. public static Bitmap getViewBitmapNoBg(View view) {
  8. view.setDrawingCacheEnabled(true);
  9. view.buildDrawingCache(true);
  10. Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
  11. // clear drawing cache
  12. view.setDrawingCacheEnabled(false);
  13. return bitmap;
  14. }
  15. /**
  16. * @param view 需要截取图片的view(含有底色)
  17. * @return Bitmap
  18. */
  19. public static Bitmap getViewBitmap(Activity activity, View view) {
  20. View screenView = activity.getWindow().getDecorView();
  21. screenView.setDrawingCacheEnabled(true);
  22. screenView.buildDrawingCache();
  23. //获取屏幕整张图片
  24. Bitmap bitmap = screenView.getDrawingCache();
  25. if (bitmap != null) {
  26. //需要截取的长和宽
  27. int outWidth = view.getWidth();
  28. int outHeight = view.getHeight();
  29. //获取需要截图部分的在屏幕上的坐标(view的左上角坐标)
  30. int[] viewLocationArray = new int[2];
  31. view.getLocationOnScreen(viewLocationArray);
  32. //从屏幕整张图片中截取指定区域
  33. bitmap = Bitmap.createBitmap(bitmap, viewLocationArray[0], viewLocationArray[1], outWidth, outHeight);
  34. }
  35. return bitmap;
  36. }

获取ViewGroup截图

 

  1. /**
  2. * @param viewGroup viewGroup
  3. * @return Bitmap
  4. */
  5. public static Bitmap getViewGroupBitmapNoBg(ViewGroup viewGroup) {
  6. // 创建对应大小的bitmap(重点)
  7. Bitmap bitmap = Bitmap.createBitmap(viewGroup.getWidth(), viewGroup.getHeight(), Bitmap.Config.ARGB_8888);
  8. Canvas canvas = new Canvas(bitmap);
  9. viewGroup.draw(canvas);
  10. return bitmap;
  11. }
  12. /**
  13. * @param viewGroup viewGroup
  14. * @return Bitmap
  15. */
  16. public static Bitmap getViewGroupBitmap(Activity activity, ViewGroup viewGroup) {
  17. View screenView = activity.getWindow().getDecorView();
  18. screenView.setDrawingCacheEnabled(true);
  19. screenView.buildDrawingCache();
  20. //获取屏幕整张图片
  21. Bitmap bitmap = screenView.getDrawingCache();
  22. if (bitmap != null) {
  23. //需要截取的长和宽
  24. int outWidth = viewGroup.getWidth();
  25. int outHeight = viewGroup.getHeight();
  26. //获取需要截图部分的在屏幕上的坐标(view的左上角坐标)
  27. int[] viewLocationArray = new int[2];
  28. viewGroup.getLocationOnScreen(viewLocationArray);
  29. //从屏幕整张图片中截取指定区域
  30. bitmap = Bitmap.createBitmap(bitmap, viewLocationArray[0], viewLocationArray[1], outWidth, outHeight);
  31. }
  32. return bitmap;
  33. }

获取Activity截图

 

  1. /**
  2. * 根据指定的Activity截图(带空白的状态栏)
  3. *
  4. * @param activity 要截图的Activity
  5. * @return Bitmap
  6. */
  7. public static Bitmap shotActivity(Activity activity) {
  8. View view = activity.getWindow().getDecorView();
  9. view.setDrawingCacheEnabled(true);
  10. view.buildDrawingCache();
  11. Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
  12. view.setDrawingCacheEnabled(false);
  13. view.destroyDrawingCache();
  14. return bitmap;
  15. }
  16. /**
  17. * 根据指定的Activity截图(去除状态栏)
  18. *
  19. * @param activity 要截图的Activity
  20. * @return Bitmap
  21. */
  22. public static Bitmap shotActivityNoStatusBar(Activity activity) {
  23. // 获取windows中最顶层的view
  24. View view = activity.getWindow().getDecorView();
  25. view.buildDrawingCache();
  26. // 获取状态栏高度
  27. Rect rect = new Rect();
  28. view.getWindowVisibleDisplayFrame(rect);
  29. int statusBarHeights = rect.top;
  30. DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
  31. // 获取屏幕长和高
  32. int widths = displayMetrics.widthPixels;
  33. int heights = displayMetrics.heightPixels;
  34. // Display display = activity.getWindowManager().getDefaultDisplay();
  35. // // 获取屏幕宽和高
  36. // int widths = display.getWidth();
  37. // int heights = display.getHeight();
  38. // 允许当前窗口保存缓存信息
  39. view.setDrawingCacheEnabled(true);
  40. // 去掉状态栏
  41. Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache(), 0,
  42. statusBarHeights, widths, heights - statusBarHeights);
  43. // 销毁缓存信息
  44. view.destroyDrawingCache();
  45. return bmp;
  46. }

对于ListView、RecyclerView等控件、长截图自行搜索截图方法。

SurfaceView截图

关于SurfaceView截屏网上也没有搜到什么解决方案,原因SurfaceView采用双缓存机制,SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。
当使用lockCanvas() 获取画布时,得到的实际上是backCanvas 而不是正在显示的 frontCanvas ,之后你在获取到的 backCanvas 上绘制新视图,再 unlockCanvasAndPost(canvas)此视图,那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas ,原来的 frontCanvas 将切换到后台作为 backCanvas 。例如,如果你已经先后两次绘制了视图A和B,那么你再调用 lockCanvas() 获取视图,获得的将是A而不是正在显示的B,之后你将重绘的 A 视图上传,那么 A 将取代 B 作为新的 frontCanvas 显示在SurfaceView 上,原来的B则转换为backCanvas。相当与多个线程,交替解析和渲染每一帧视频数据 --引用https://www.jianshu.com/p/a2a235bee59e

普通View onDraw 内容是静态的,不调invalidate() 它是不会发生变化,你可以拿到里面的Bitmap;但是SurfaceView不同,无法拿到它back buffer里面的Bitmap。

官方注释:

 

  1. * <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call
  2. * to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a
  3. * bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when
  4. * the cache is enabled. To benefit from the cache, you must request the drawing cache by
  5. * calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not
  6. * null.</p>

如果你用普通view的截图方法去获取截图,老铁你看到的图片是这样的

 

  1. 图片是黑色

这里我们可以采用另一种思路去实现,所谓条条道路通罗马吗,我们可以从视频源来获取截图,反正视频源的图片也是一帧一帧的渲染到SurfaceView上面,获取截图之后,获取SurfaceView控件的宽高参数设置到截图上,这里涉及到另外一种情况,就是SurfaceView控件上面还有View控件,这种可以将普通控件Bitmap图片与SurfaceView截图合成一张图片,这样就完美截图SurfaceView附近的截图。

image

获取SurfaceView控件视频截图

点击截图时,获取MediaPlayer 当前一张视频图片

 

  1. /**
  2. * @param uri 视频的本地路径
  3. * @param context 上下文
  4. * @param mediaPlayer 媒体播放Player
  5. * @return Bitmap 返回的视频图像
  6. */
  7. public static Bitmap getVideoFrame(Context context, Uri uri, MediaPlayer mediaPlayer) {
  8. Bitmap bmp = null;
  9. // android 2.3及其以上版本使用
  10. MediaMetadataRetriever retriever = new MediaMetadataRetriever();
  11. try {
  12. retriever.setDataSource(context, uri);
  13. // 这一句是必须的
  14. String timeString =
  15. retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
  16. // 获取总长度,这一句也是必须的
  17. long titalTime = Long.parseLong(timeString) * 1000;
  18. int duration = mediaPlayer.getDuration();
  19. // 通过这个计算出想截取的画面所在的时间
  20. long videoPosition = titalTime * mediaPlayer.getCurrentPosition() / duration;
  21. if (videoPosition > 0) {
  22. bmp = retriever.getFrameAtTime(videoPosition,
  23. MediaMetadataRetriever.OPTION_CLOSEST);
  24. }
  25. } catch (IllegalArgumentException ex) {
  26. ex.printStackTrace();
  27. } catch (RuntimeException ex) {
  28. ex.printStackTrace();
  29. } finally {
  30. try {
  31. retriever.release();
  32. } catch (RuntimeException e) {
  33. e.fillInStackTrace();
  34. }
  35. }
  36. return bmp;
  37. }

获取SurfaceView控件照片截图

点击截图时,获取Camera当前一张照片,类似于拍照

 

  1. camera.takePicture(null, null, new Camera.PictureCallback() {
  2. @Override
  3. public void onPictureTaken(byte[] data, Camera camera) {
  4. Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
  5. Matrix matrix = new Matrix();
  6. //旋转照片
  7. matrix.setRotate(360 - 90);
  8. matrix.postScale(-1, 1);
  9. bitmap = createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  10. if (takePhotoCallBack != null) {
  11. takePhotoCallBack.takePhotoCallBack(bitmap);
  12. }
  13. //拍照完成继续
  14. startCamera(holder);
  15. }
  16. });

获取SurfaceView控件自定义截图

一般SurfaceView的视频源不仅限于Camera、Media,还有其他乱七八糟的视频源,比如opencv等,这个时候我们可以获取SurfaceView的Canvas,这样我们可以将Canvas内容转换成Bitmap。

 

  1. public abstract class AbstractMySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
  2. SurfaceHolder surfaceHolder;
  3. public AbstractMySurfaceView(Context context) {
  4. super(context);
  5. surfaceHolder = this.getHolder();
  6. surfaceHolder.addCallback(this);
  7. }
  8. @Override
  9. public void surfaceChanged(SurfaceHolder holder, int format, int width,
  10. int height) {
  11. }
  12. @Override
  13. public void surfaceCreated(SurfaceHolder holder) {
  14. new Thread(new MyRunnable()).start();
  15. }
  16. @Override
  17. public void surfaceDestroyed(SurfaceHolder holder) {
  18. }
  19. class MyRunnable implements Runnable {
  20. @Override
  21. public void run() {
  22. Canvas canvas = surfaceHolder.lockCanvas(null);//获取画布
  23. doDraw(canvas);
  24. surfaceHolder.unlockCanvasAndPost(canvas);//解锁画布
  25. }
  26. }
  27. //将绘制方法抽象出来供子类实现
  28. protected abstract void doDraw(Canvas canvas);
  29. //将oDraw绘制在自己的canvas上
  30. public Bitmap getBitmap() {
  31. Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
  32. Canvas canvas = new Canvas(bitmap);
  33. doDraw(canvas);
  34. return bitmap;
  35. }
  36. }
  37. mAbstractMySurfaceView = new AbstractMySurfaceView(this) {
  38. @Override
  39. protected void doDraw(Canvas canvas) {
  40. Paint paint = new Paint();
  41. paint.setColor(Color.YELLOW);
  42. canvas.drawRect(new RectF(100, 100, 1000, 500), paint);
  43. }
  44. };

将opencv的视频一帧一帧绘制到Canvas上,从而获取Canvas内容转化成Bitmap。

 

  1. org.opencv.core.Rect rectOC = new org.opencv.core.Rect(x, y, width, height);
  2. Rect rect = new Rect(rectOC.x, rectOC.y, rectOC.x + rectOC.width, rectOC.y + rectOC.height);
  3. canvas.drawRect(rect, mPaint);

整个屏幕截屏(必杀技)

Android 5.0以上的版本才支持整个屏幕截屏,这样就不管啥控件屏幕上你能看到都能截取出来,这里为了大家方便调用对其进行封装。

 

  1. ScreenCapture screenCapture = new ScreenCapture(this);
  2. Bitmap captureBitmap = screenCapture.getCaptureBitmap();

这个参照了Rxpermission的思想进行了封装,使屏幕截图更加方便,以后添加屏幕录制功能后,将这个封装抽出来作为一个库。

总结

Android普通View控件截屏一般没什么问题,网上一搜一大把,对于SurfaceView截屏就有点困难了,一般方式的截图都是黑屏,我们可以采用三种思路:
1、获取源头视频的截图作为SurfaceView的截图
2、获取SurfaceView的画布canvas,将canvas保存成Bitmap
3、直接截取整个屏幕,然后在截图SurfaceView位置的图

声明:

转载请注明出处
作者:戎码虫
链接:https://www.jianshu.com/p/fb140250a500

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/261995
推荐阅读
相关标签
  

闽ICP备14008679号