Android利用SurfaceView显示Camera图像爬坑记(四)

前言

通过前面几篇,利用SurfaceView显示Camera的图像已经没什么问题了,接下来我们就要打磨一下细节,主要就是手机旋转的问题,考虑到我们会用横屏和竖屏的不同的情况。

横竖屏问题

用我们前面的DEMO后,因为默认的打开后就是竖屏,所以进入后图像没有问题,如下

但是我们切换到横屏后,图像就显示不对了,如下

原来的代码中我们也只是解决了默认竖屏的问题

在StartCamera中,设置了直接旋转90度(不过我们改为SurfaceTexture这样显示后,这个没什么用了)

然后生成图像的方法nv21ToBitmap中加入了旋转90度

实现思路

  1. 获取到手机当前的旋转角度

  2. 根据当前旋转的角度修改生成图像的角度

  3. 生成的图像后加入角度旋转

代码实现

首先把setDisplayOrientation这个屏蔽掉了,因为已经没用了

获取到手机当前的旋转角度

想要获取手机的当前旋转角度,就要用到WindowManager,所以我们要加入WindowManager的定义,还要加入一个默认的角度值。然后在构造函数中获取到当前windowmanager。

根据当前旋转的角度修改生成图像的角度

记得当时有三个重写的参数surfaceCreated,surfaceChanged和surfaceDestoryed,其中我们在surfaceCreated中加入了打开Camera,在surfaceDestoryed中加入了关闭Camera,而surfaceChanged中什么也没写,这次这个方法就被我们用到了。

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        int angel = windowManager.getDefaultDisplay().getRotation();
        switch (angel) {
            case Surface.ROTATION_0:
                rotatedegree=90;
                break;
            case Surface.ROTATION_90:
                rotatedegree=0;
                break;
            case Surface.ROTATION_180:
                rotatedegree=270;
                break;
            case Surface.ROTATION_270:
                rotatedegree=180;
                break;
            default:
                rotatedegree=0;
                break;
        }
    }

上面就是通过判断手机旋转角度,改变我们定义的retatedegree的值,这个是我根据旋转的情况自己算出来的,生成图像旋转的角度。

生成的图像后加入角度旋转

最后在我们nv21ToBitmap中的旋转角度改为我们已经定义的retatedegree

视频效果

基本到这,我们这个SurfaceView调Camera的类就完成了,这里我把整个类的代码放到这里,后面可能会在这上面加入通过OpenCV NDK的图像处理,这里做一个备份吧。

package dem.vac.surfaceviewdemo;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;


import java.io.ByteArrayOutputStream;
import java.io.IOException;


/**
 * 作者:Vaccae
 * 邮箱:3657447@qq.com
 * 创建时间:2019-06-12 15:42
 * 功能模块说明:
 */
public class VaccaeSurfaceView extends SurfaceView implements SurfaceHolder.Callback {


    //Camera相关
    //设置摄像头ID,我们默认为后置
    private int mCameraIndex=Camera.CameraInfo.CAMERA_FACING_BACK;
    //定义摄像机
    private Camera camera;
    //定义Camera的回调方法
    private Camera.PreviewCallback previewCallback=new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] bytes, Camera camera) {
            synchronized (this) {
                int width=camera.getParameters().getPreviewSize().width;
                int height=camera.getParameters().getPreviewSize().height;
                Log.d("frame", "width:"+width+" height:"+height);
                Canvas canvas=holder.lockCanvas();
                if (canvas != null) {
                    Log.i("frame", "canvas width:" + canvas.getWidth() + " height:" + canvas.getHeight());
                    canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                    Bitmap cacheBitmap=nv21ToBitmap(bytes, width, height);


                    RectF rectF=new RectF(0, 0, canvas.getWidth(), canvas.getHeight());
                    canvas.drawBitmap(cacheBitmap, null, rectF, null);


                    holder.unlockCanvasAndPost(canvas);
                }
            }
        }
    };


    //定义SurfaceHolder
    private SurfaceHolder holder;
    //定义SurfaceTexture;
    private SurfaceTexture surfaceTexture;


    //WindowManager 用于获取摄像机方向
    private WindowManager windowManager;
    //当前相机角度
    private int rotatedegree=0;




    //构造函数
    public VaccaeSurfaceView(Context context) {
        super(context);


        //获取WindowManager
        windowManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);


        //实例化Surfacetexture
        surfaceTexture=new SurfaceTexture(2);
        //获取Holder
        holder=getHolder();
        //加入SurfaceHolder.Callback在类中implements
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        //保持屏幕常亮
        holder.setKeepScreenOn(true);
    }


    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        //开启摄像机
        startCamera(mCameraIndex);
    }


    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        int angel = windowManager.getDefaultDisplay().getRotation();
        switch (angel) {
            case Surface.ROTATION_0:
                rotatedegree=90;
                break;
            case Surface.ROTATION_90:
                rotatedegree=0;
                break;
            case Surface.ROTATION_180:
                rotatedegree=270;
                break;
            case Surface.ROTATION_270:
                rotatedegree=180;
                break;
            default:
                rotatedegree=0;
                break;
        }
    }


    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        //关闭摄像机
        stopCamera();
    }


    //region 开启关闭Camera
    //开启摄像机
    private void startCamera(int mCameraIndex) {
        // 初始化并打开摄像头
        if (camera == null) {
            try {
                camera=Camera.open(mCameraIndex);
            } catch (Exception e) {
                return;
            }
            //获取Camera参数
            Camera.Parameters params=camera.getParameters();
            if (params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                // 自动对焦
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            }
            params.setPreviewFormat(ImageFormat.NV21); // 设置预览图片格式
            params.setPictureFormat(ImageFormat.JPEG); // 设置拍照图片格式


            camera.setParameters(params);


            try {
                camera.setPreviewCallback(previewCallback);
                camera.setPreviewTexture(surfaceTexture);
//                camera.setPreviewDisplay(holder);
                //旋转90度
//                camera.setDisplayOrientation(90);
                camera.startPreview();
            } catch (Exception ex) {
                ex.printStackTrace();
                camera.release();
                camera=null;
            }
        }
    }


    //关闭摄像机
    private void stopCamera() {
        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
            camera=null;
        }


    }
    //endregion


    private Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
        Bitmap bitmap=null;
        try {
            YuvImage image=new YuvImage(nv21, ImageFormat.NV21, width, height, null);
            ByteArrayOutputStream stream=new ByteArrayOutputStream();
            image.compressToJpeg(new Rect(0, 0, width, height), 80, stream);
            //将rawImage转换成bitmap
            BitmapFactory.Options options=new BitmapFactory.Options();
            options.inPreferredConfig=Bitmap.Config.ARGB_8888;
            bitmap=BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size(), options);


            //加入图像旋转
            Matrix m=new Matrix();
            m.postRotate(rotatedegree);
            bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                    m, true);


            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }






}


-END-

Vaccae的往期经典


OpenCV

《C++ OpenCV案例实战---卡号获取

《C++ OpenCV案例实战---卡片截取(附代码)

《C++ OpenCV透视变换---切换手机正面图片》

《C++ OpenCV实战---获取数量

《C++ OpenCV实战---利用颜色分割获取数量》


Android

《Android利用SurfaceView结合科大讯飞修改语音实别UI

《Android关于语音识别的功能实现分析(一)---结构化思维》

《Android关于语音识别的功能实现分析(二)---语义解析》

《Android根据类生成签名字符串

《Android碎片化布局fragment的实战应用

《Android中RecyclerView嵌套RecyclerView

《Android里用AsyncTask后的接口回调


.Net C#

《C#自定义特性(Attribute)讲解与实际应用

《C#根据类生成签名字符串(附DEMO下载地址)

《C++创建动态库C#调用》

《C#与三菱PLC(型号FX2N)串口通讯类


数据库及其它

《Oracel存储过程写报表实战》

《Delphi轮播视频和图片程序(用于双屏显示程序)

《SQL随机增加销售数据的脚本编写(附脚本下载地址)

SQL Server中With As的介绍与应用(三)--递归的实战应用

《Oracle通过ODBC连接SQL Server数据库


长按下方二维码关注微卡智享

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值