Android 自定义Camera + TextureView拍照

Android 自定义Camera + TextureView拍照#

自定义camera需要注意这几点:

  1. camera预览的角度。
  2. textureview的宽高比和camera预览时设置的宽高比。
  3. 拍照之后图片的旋转角度。

在自定义相机之前可以看下这篇文章,了解一下相机传感器的方向问题https://blog.csdn.net/c10WTiybQ1Ye3/article/details/78098459

在解决详解预览的角度问题,官方有一个推荐的写法。

  /**
 * 保证预览方向正确
 *
 * @param context
 * @param cameraId
 * @param camera
 */
public void setCameraDisplayOrientation(Activity context,
                                        int cameraId, Camera camera) {
    android.hardware.Camera.CameraInfo info =
            new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    int rotation = context.getWindowManager().getDefaultDisplay()
            .getRotation();
    int degrees = 0;
    switch (rotation) {
        case Surface.ROTATION_0: degrees = 0; break;
        case Surface.ROTATION_90: degrees = 90; break;
        case Surface.ROTATION_180: degrees = 180; break;
        case Surface.ROTATION_270: degrees = 270; break;
    }

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;  // compensate the mirror
    } else {  // back-facing
        result = (info.orientation - degrees + 360) % 360;
    }

    camera.setDisplayOrientation(result);
}

在预览是如果宽高设置的不恰当不仅可能会出现崩溃的情况,还会出现预览界面变形的情况,为了解决变形的情况,我们应该让预览界面的宽高比例和textureview的宽高比例尽量达到一致,同时在解决因为宽高设置的不恰当问题,我们也应该在相机支持的分辨率中去寻找。我为了更方便的控制宽高的比例,我设置的textureview的宽高是充满屏幕的。那么屏幕的宽高比例,就是相机分辨率的宽高比例。注意:因为相机传感器是横屏安装的,所以应该用屏幕的h/w,来确定设置的分辨率的w/h

 /**
 *
 *
 * @param sizes 相机支持的size
 * @param targetRatio h/w
 * @param comparator 升序或者降序
 * @param minWidth 最小的支持宽度
 * @return
 */
public Camera.Size getOptimalPreviewSize(List sizes, float targetRatio, int comparator, int minWidth) {
    if (sizes == null)
        return null;
    Camera.Size optimalSize = null;


	// 对size进行排序
    Collections.sort(sizes, getComparator(comparator));

    //先找出宽度大于最小宽度的size
    List tempList = new ArrayList<>();
    for(Camera.Size size : sizes){
        Log.i("sss", "....width.....:"+size.width+"...height.."+size.height);
        if(size.width >= minWidth){
            tempList.add(size);
        }
    }

    if(tempList.size() > 0){
        // 找比例相同的,这里是整个屏幕的高和宽的比。
        for (Camera.Size size : tempList) {
            float currentRatio = ((float) size.width) / size.height;
            if (currentRatio - targetRatio == 0) {
                optimalSize = size;
                break;
            }
        }
    }else{
        //比例相同的
        for (Camera.Size size : sizes) {
            float currentRatio = ((float) size.width) / size.height;
            if (currentRatio - targetRatio == 0) {
                optimalSize = size;
                break;
            }
        }
    }

    // 如果没有就找个相近的
    if(optimalSize == null){
        float tempRation;
        float minRation = Float.MAX_VALUE;
        if(tempList.size() > 0){
            for (Camera.Size size : tempList) {
                float curRatio = ((float) size.width) / size.height;
                tempRation = Math.abs(targetRatio - curRatio);
                if(tempRation 

拍照之后图片旋转角度,对于图片的处理可以参考这片文章https://blog.csdn.net/Lamphogani/article/details/79197015?utm_source=blogxgwz9
我想要的要效果是图片的方向跟着手机的方向来设定,因此我使用的是

  orientationEventListener = new OrientationEventListener(context) {
        @Override
        public void onOrientationChanged(int orientation) {
            if (ORIENTATION_UNKNOWN == orientation) {
                return;
            }
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(cameraId, info);

            orientation = (orientation + 45) / 90 * 90;
            rotation = 0;
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                rotation = (info.orientation - orientation + 360) % 360;
            } else {
                rotation = (info.orientation + orientation) % 360;
            }
			// 我不知道怎么回事,我的这个设置并没起到在拍照之后图片旋转的效果
            if (null != mCamera) {
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setRotation(rotation);
                mCamera.setParameters(parameters);
            }
        }
    };

我只是在这记录拍照是的手机的角度,之后对图片进行的旋转

 /**
 * 把相机拍照返回照片转正
 *
 * @param angle 旋转角度
 * @return bitmap 图片
 */
public  Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
    //旋转图片 动作
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    //加入翻转 把相机拍照返回照片转正
    if (id == 1) {
        matrix.postScale(-1, 1);
    }
    // 创建新的图片, 如果传入的角度是bitmap当前的角度的话,
    // 就不会做重新生成一个新的bitmap。只是把bitmap的值给了resizeBitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
            bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    if(resizedBitmap == null){
        resizedBitmap = bitmap;
    }
  
    return resizedBitmap;
}

注意到以上几个问题就差不多了,剩下的就是具体的自定义了。

首先做简单的设置,因为我使用的是camera+textureview,所以应该开启窗口加速,我在setcontentview中设置了

  //此行代码必须存在,是TextureView必要在窗口加速中才能使用
    getWindow().setFlags(
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

    requestWindowFeature(Window.FEATURE_NO_TITLE); //设置无标题

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);  //设置全屏
    this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//拍照过程屏幕一直处于高亮
    //设置手机屏幕朝向,一共有7种
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

然后就是权限的申请,同时也注册了textureView.setSurfaceTextureListener(this);在权限申请通过和textureview创建成功之后,就可以初始化camera同时开启camera的预览了

`  // 权限
String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.CAMERA,
        Manifest.permission.RECORD_AUDIO
};`

初始化camera

`/**
 *
 * @param surfaceTexture
 * @param cameraId
 */
@Override
public void startPreview(SurfaceTexture surfaceTexture, int cameraId) {

    this.cameraId = cameraId;
    this.mSurfaceTexture = surfaceTexture;

    if (mCamera == null && mSurfaceTexture != null) {

        if(orientationEventListener != null){
            orientationEventListener.enable();
        }

        // 获取camera实例对象
        mCamera = getCameraInstance(cameraId);
        // 开启预览
        try {
            if (mCamera != null) {
                mCamera.setPreviewTexture(mSurfaceTexture);
                mCamera.lock();
                setCameraDisplayOrientation(context, cameraId, mCamera);

                initCameraParameters();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


/** 安全获取Camera对象实例的方法 */
private Camera getCameraInstance(int cameraId) {
    try {
        mCamera = Camera.open(cameraId); // 试图获取Camera实例
    }
    catch (Exception e) {
        // 摄像头不可用(正被占用或不存在)
    }
    return mCamera; // 不可用则返回null
}


` /**
 * 初始化摄像头参数
 */
private void initCameraParameters() {
    // 初始化摄像头参数
    mParameters = mCamera.getParameters();
    mCamera.lock();

    List focusModes = mParameters.getSupportedFocusModes();
    // 设置对焦模式
    if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
        // Autofocus mode is supported 自动对焦
        mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    }
    if(focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)){
        mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦
    }
//         预览尺寸
    previewSize = getOptimalPreviewSize(mParameters.getSupportedPreviewSizes(), DisplayUtils.getScreenRate(context), Constants.COMPARATOR_ASCEND, 1280);
    if (previewSize != null) {
        mParameters.setPreviewSize(previewSize.width, previewSize.height);
    }
    // 图片尺寸
    Camera.Size pictrueSize = getOptimalPreviewSize(mParameters.getSupportedPictureSizes(), DisplayUtils.getScreenRate(context), Constants.COMPARATOR_ASCEND, 1280);
    if (pictrueSize != null) {
        mParameters.setPictureSize(pictrueSize.width, pictrueSize.height);
    }
    //设置预览格式
    mParameters.setPreviewFormat(ImageFormat.NV21);

    try {
        mCamera.setParameters(mParameters);
        mCamera.setPreviewCallback(mRecordingUtils);
        mCamera.startPreview();
        // 2如果要实现连续的自动对焦,这一句必须加上
        mCamera.cancelAutoFocus();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

`

最后就是camera的拍照操作了。。。拍照有两个方法,一个是从camera.setPreviewCallback的监听中,获取拍照时的流,然后生成图片,另一种就是通过camera的takePicture方法获取。我使用的是第二种。虽然后面也的使用第一种监听中的方法进行视屏的录制。

mCamera.takePicture(null, null, new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(final byte[] data, Camera camera) {

            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
					//进行了图片的旋转,因为是耗时所以我放在了子线程中去做
                    Bitmap savebitmap = rotaingImageView(cameraId, tempRotation, bitmap);

                    String img_path = imagePath;
                    if(TextUtils.isEmpty(img_path)){
                        img_path = Constants.DEFAULT_DIRECTORY;
                    }

                    img_path = img_path + "/" + System.currentTimeMillis() + ".jpeg";
                    File file = BitmapUtils.saveJPGE_After(context, savebitmap, img_path, 100);

                    if(cameraResultCallBack != null){
                        cameraResultCallBack.takePhotoResult(file);
                    }

					if(bitmap != null && !bitmap.isRecycled()){
					    bitmap.recycle();
					}

                    if(savebitmap != null && !savebitmap.isRecycled()){
                        savebitmap.recycle();
                        savebitmap = null;
                    }

                }
            }).start();

            mCamera.startPreview();
        }
    });
``

以上就是自定义camera的基本操作流程

你可能感兴趣的:(android,笔记)