赞
踩
image
获取View截图
- /**
- * 获取控件截图(黑色背景)
- *
- * @param view view
- * @return Bitmap
- */
- public static Bitmap getViewBitmapNoBg(View view) {
- view.setDrawingCacheEnabled(true);
- view.buildDrawingCache(true);
- Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
- // clear drawing cache
- view.setDrawingCacheEnabled(false);
- return bitmap;
- }
-
- /**
- * @param view 需要截取图片的view(含有底色)
- * @return Bitmap
- */
- public static Bitmap getViewBitmap(Activity activity, View view) {
- View screenView = activity.getWindow().getDecorView();
- screenView.setDrawingCacheEnabled(true);
- screenView.buildDrawingCache();
-
- //获取屏幕整张图片
- Bitmap bitmap = screenView.getDrawingCache();
-
- if (bitmap != null) {
- //需要截取的长和宽
- int outWidth = view.getWidth();
- int outHeight = view.getHeight();
-
- //获取需要截图部分的在屏幕上的坐标(view的左上角坐标)
- int[] viewLocationArray = new int[2];
- view.getLocationOnScreen(viewLocationArray);
-
- //从屏幕整张图片中截取指定区域
- bitmap = Bitmap.createBitmap(bitmap, viewLocationArray[0], viewLocationArray[1], outWidth, outHeight);
- }
- return bitmap;
- }
-
获取ViewGroup截图
- /**
- * @param viewGroup viewGroup
- * @return Bitmap
- */
- public static Bitmap getViewGroupBitmapNoBg(ViewGroup viewGroup) {
- // 创建对应大小的bitmap(重点)
- Bitmap bitmap = Bitmap.createBitmap(viewGroup.getWidth(), viewGroup.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- viewGroup.draw(canvas);
- return bitmap;
- }
-
- /**
- * @param viewGroup viewGroup
- * @return Bitmap
- */
- public static Bitmap getViewGroupBitmap(Activity activity, ViewGroup viewGroup) {
- View screenView = activity.getWindow().getDecorView();
- screenView.setDrawingCacheEnabled(true);
- screenView.buildDrawingCache();
-
- //获取屏幕整张图片
- Bitmap bitmap = screenView.getDrawingCache();
-
- if (bitmap != null) {
- //需要截取的长和宽
- int outWidth = viewGroup.getWidth();
- int outHeight = viewGroup.getHeight();
-
- //获取需要截图部分的在屏幕上的坐标(view的左上角坐标)
- int[] viewLocationArray = new int[2];
- viewGroup.getLocationOnScreen(viewLocationArray);
-
- //从屏幕整张图片中截取指定区域
- bitmap = Bitmap.createBitmap(bitmap, viewLocationArray[0], viewLocationArray[1], outWidth, outHeight);
- }
- return bitmap;
- }
-
获取Activity截图
- /**
- * 根据指定的Activity截图(带空白的状态栏)
- *
- * @param activity 要截图的Activity
- * @return Bitmap
- */
- public static Bitmap shotActivity(Activity activity) {
- View view = activity.getWindow().getDecorView();
- view.setDrawingCacheEnabled(true);
- view.buildDrawingCache();
- Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
- view.setDrawingCacheEnabled(false);
- view.destroyDrawingCache();
- return bitmap;
- }
-
- /**
- * 根据指定的Activity截图(去除状态栏)
- *
- * @param activity 要截图的Activity
- * @return Bitmap
- */
- public static Bitmap shotActivityNoStatusBar(Activity activity) {
- // 获取windows中最顶层的view
- View view = activity.getWindow().getDecorView();
- view.buildDrawingCache();
- // 获取状态栏高度
- Rect rect = new Rect();
- view.getWindowVisibleDisplayFrame(rect);
- int statusBarHeights = rect.top;
-
- DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
- // 获取屏幕长和高
- int widths = displayMetrics.widthPixels;
- int heights = displayMetrics.heightPixels;
-
- // Display display = activity.getWindowManager().getDefaultDisplay();
- // // 获取屏幕宽和高
- // int widths = display.getWidth();
- // int heights = display.getHeight();
- // 允许当前窗口保存缓存信息
- view.setDrawingCacheEnabled(true);
- // 去掉状态栏
- Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache(), 0,
- statusBarHeights, widths, heights - statusBarHeights);
- // 销毁缓存信息
- view.destroyDrawingCache();
- return bmp;
- }
-
对于ListView、RecyclerView等控件、长截图自行搜索截图方法。
关于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。
官方注释:
- * <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call
- * to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a
- * bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when
- * the cache is enabled. To benefit from the cache, you must request the drawing cache by
- * calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not
- * null.</p>
-
如果你用普通view的截图方法去获取截图,老铁你看到的图片是这样的
- 图片是黑色
-
这里我们可以采用另一种思路去实现,所谓条条道路通罗马吗,我们可以从视频源来获取截图,反正视频源的图片也是一帧一帧的渲染到SurfaceView上面,获取截图之后,获取SurfaceView控件的宽高参数设置到截图上,这里涉及到另外一种情况,就是SurfaceView控件上面还有View控件,这种可以将普通控件Bitmap图片与SurfaceView截图合成一张图片,这样就完美截图SurfaceView附近的截图。
image
获取SurfaceView控件视频截图
点击截图时,获取MediaPlayer 当前一张视频图片
- /**
- * @param uri 视频的本地路径
- * @param context 上下文
- * @param mediaPlayer 媒体播放Player
- * @return Bitmap 返回的视频图像
- */
- public static Bitmap getVideoFrame(Context context, Uri uri, MediaPlayer mediaPlayer) {
- Bitmap bmp = null;
- // android 2.3及其以上版本使用
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- try {
- retriever.setDataSource(context, uri);
- // 这一句是必须的
- String timeString =
- retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
- // 获取总长度,这一句也是必须的
- long titalTime = Long.parseLong(timeString) * 1000;
-
- int duration = mediaPlayer.getDuration();
- // 通过这个计算出想截取的画面所在的时间
- long videoPosition = titalTime * mediaPlayer.getCurrentPosition() / duration;
- if (videoPosition > 0) {
- bmp = retriever.getFrameAtTime(videoPosition,
- MediaMetadataRetriever.OPTION_CLOSEST);
- }
- } catch (IllegalArgumentException ex) {
- ex.printStackTrace();
- } catch (RuntimeException ex) {
- ex.printStackTrace();
- } finally {
- try {
- retriever.release();
- } catch (RuntimeException e) {
- e.fillInStackTrace();
- }
- }
- return bmp;
- }
-
获取SurfaceView控件照片截图
点击截图时,获取Camera当前一张照片,类似于拍照
- camera.takePicture(null, null, new Camera.PictureCallback() {
- @Override
- public void onPictureTaken(byte[] data, Camera camera) {
- Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
- Matrix matrix = new Matrix();
- //旋转照片
- matrix.setRotate(360 - 90);
- matrix.postScale(-1, 1);
- bitmap = createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
- if (takePhotoCallBack != null) {
- takePhotoCallBack.takePhotoCallBack(bitmap);
- }
- //拍照完成继续
- startCamera(holder);
- }
- });
-
获取SurfaceView控件自定义截图
一般SurfaceView的视频源不仅限于Camera、Media,还有其他乱七八糟的视频源,比如opencv等,这个时候我们可以获取SurfaceView的Canvas,这样我们可以将Canvas内容转换成Bitmap。
- public abstract class AbstractMySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
- SurfaceHolder surfaceHolder;
-
- public AbstractMySurfaceView(Context context) {
- super(context);
- surfaceHolder = this.getHolder();
- surfaceHolder.addCallback(this);
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- new Thread(new MyRunnable()).start();
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- }
-
- class MyRunnable implements Runnable {
- @Override
- public void run() {
- Canvas canvas = surfaceHolder.lockCanvas(null);//获取画布
- doDraw(canvas);
- surfaceHolder.unlockCanvasAndPost(canvas);//解锁画布
- }
- }
-
- //将绘制方法抽象出来供子类实现
- protected abstract void doDraw(Canvas canvas);
-
- //将oDraw绘制在自己的canvas上
- public Bitmap getBitmap() {
- Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- doDraw(canvas);
- return bitmap;
- }
- }
-
- mAbstractMySurfaceView = new AbstractMySurfaceView(this) {
- @Override
- protected void doDraw(Canvas canvas) {
- Paint paint = new Paint();
- paint.setColor(Color.YELLOW);
- canvas.drawRect(new RectF(100, 100, 1000, 500), paint);
- }
- };
-
将opencv的视频一帧一帧绘制到Canvas上,从而获取Canvas内容转化成Bitmap。
- org.opencv.core.Rect rectOC = new org.opencv.core.Rect(x, y, width, height);
- Rect rect = new Rect(rectOC.x, rectOC.y, rectOC.x + rectOC.width, rectOC.y + rectOC.height);
- canvas.drawRect(rect, mPaint);
-
整个屏幕截屏(必杀技)
Android 5.0以上的版本才支持整个屏幕截屏,这样就不管啥控件屏幕上你能看到都能截取出来,这里为了大家方便调用对其进行封装。
- ScreenCapture screenCapture = new ScreenCapture(this);
- Bitmap captureBitmap = screenCapture.getCaptureBitmap();
-
这个参照了Rxpermission的思想进行了封装,使屏幕截图更加方便,以后添加屏幕录制功能后,将这个封装抽出来作为一个库。
Android普通View控件截屏一般没什么问题,网上一搜一大把,对于SurfaceView截屏就有点困难了,一般方式的截图都是黑屏,我们可以采用三种思路:
1、获取源头视频的截图作为SurfaceView的截图
2、获取SurfaceView的画布canvas,将canvas保存成Bitmap
3、直接截取整个屏幕,然后在截图SurfaceView位置的图
声明:
转载请注明出处
作者:戎码虫
链接:https://www.jianshu.com/p/fb140250a500
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。