Andriod - Camera笔记

文章目录

  • 前言
  • 一、Camera预览和拍照步骤
    • 第一步:申请相机权限和存储读写权限
    • 第二步:查找需要打开的摄像头是否存在
    • 第三步:通过摄像头ID打开指定的摄像头
    • 第四步:设置摄像头参数
      • 1. Camera.Parameters参数(非必须)
        • 创建Camera.Parameters对象,可以获取当前默认参数,也可以设置指定的参数
        • 常用参数设置:
          • 设置预览大小
          • 设置图片大小,拍照
          • 设置预览格式,所有的相机都支持 NV21格式
      • 2. Camera其他参数
        • 1.预览视图(必须)
        • 2.设置相机预览方向(非必须)
        • 3.添加预览数据监听器(必须)
    • 第五步:打开预览
    • 第六步:拍照
  • 二、Camera拍照实例代码
  • 总结


前言

自己总结的拍照步骤,网上类似的代码很多,但是没有写第一步干什么第二步干什么,我看的云里雾里


一、Camera预览和拍照步骤

第一步:申请相机权限和存储读写权限

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

第二步:查找需要打开的摄像头是否存在

1.创建CameraInfo对象,CaneraInfo里面装的就是设备的摄像头信息

Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

2.CaneraInfo里面装的就是设备的摄像头信息,这里列一下常用的方法
因为设备可能有多个摄像头,所以先获取设备上的摄像头数量

int cameraCount = Camera.getNumberOfCameras();

获取摄像头朝向,摄像头朝向有两个方向,1代表前摄,0代表后摄

int facing = cameraInfo.facing

3.获取摄像头图像方向,也就是摄像机图像需要顺时针旋转的角度,以便以正确的方向正确显示在显示器上。 具体的值为0,90,180或270

int orientation = cameraInfo.orientation

4.摄像头是否可以禁用快门声音
如果此字段为true,则enableShutterSound(false)的呼叫将成功。 如果设置为false,则该呼叫将失败,并且当takePicture时会播放快门声音。

boolean DisableShutterSound= cameraInfo.canDisableShutterSound

第三步:通过摄像头ID打开指定的摄像头

Camera camera = Camera.open(摄像头id)

第四步:设置摄像头参数

1. Camera.Parameters参数(非必须)

创建Camera.Parameters对象,可以获取当前默认参数,也可以设置指定的参数
Camera.Parameters parameters = camera.getParameters();
常用参数设置:
设置预览大小
parameters.setPreviewSize(1920, 1080);
设置图片大小,拍照
parameters.setPictureSize(1920, 1080);
设置预览格式,所有的相机都支持 NV21格式
parameters.setPreviewFormat(ImageFormat.NV21);

parameters中的参数可以不设置也行,但是如果设置了parameters参数的话必须要加上这一行代码,否则设置不会生效:

 camera.setParameters(parameters);

2. Camera其他参数

1.预览视图(必须)

这个参数是必须要设置的,否则无法预览,最简单的方法:layout中添加SurfaceView控件,这里直接引用就行

camera.setPreviewDisplay(surfaceView.getHolder());
2.设置相机预览方向(非必须)

首先获取设备当前横竖屏状态

int rotation = getWindowManager().getDefaultDisplay().getRotation();

然后计算正确的方向

int result = 0;
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
	//后置摄像头
	result = (cameraInfo.orientation - rotation + 360) % 360;
	
} else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
	//前置摄像头,多一步镜像
	result = (cameraInfo.orientation + rotation ) % 360;
	result = (360 - result) % 360;
}

最后将正确的方向设置到摄像头中

camera.setDisplayOrientation(result);
3.添加预览数据监听器(必须)

下面这个监听器也是必须要加的,不加的话预览画面会卡住不动,这个监听器有一个参数byte[] data就是预览的图像数据,如果需要做二维码扫描,AI识别这些功能的话就可以从这里取这个数据用了

    Camera.PreviewCallback  preCallback = new Camera.PreviewCallback(){

        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            camera.addCallbackBuffer(data);
        }
    };

然后将监听器添加到相机中

camera.setPreviewCallbackWithBuffer(preCallback);

还有其他参数就不写了,我也不懂,反正现在这样已经能用了

第五步:打开预览

上面的参数设置完后就一行代码打开预览ok

camera.startPreview();

第六步:拍照

拍照注意事项:
1.必须是已经打开了预览的时候才可以拍照,否者拍不了
2.拍照的时候预览会自动停止,但是拍完照后预览不会自动打开,所以需要在拍照之后加一行打开预览的代码

拍照步骤:
1.创建一个异步线程用来处理拍摄的照片数据
2.创建一个拍照的回调,Camera.PictureCallback(),重写回调中的onPictureTaken()方法,在这个方法中打开处理数据的异步线程处理数据
3.将拍照的回调添加到拍照的方法中
camera.takePicture(null, null, 回调程序 )

camera.takePicture(null, null, new Camera.PictureCallback() {
	@Override
    public void onPictureTaken(byte[] data, Camera camera) {
    	MyAsyncTask myTask = new MyAsyncTask(MainActivity.this, cameraInfo.facing, data,camera);
        myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
	}
});

二、Camera拍照实例代码

MainActivity.java

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener, Camera.PreviewCallback {
    private SurfaceView surfaceView;
    private Button photograph;
    private Button recording;
    private Button switchs;
    private int rotation;
    private int shortSize;
    private int longSize;
    private Camera camera;
    private Camera.CameraInfo cameraInfo;
    private Camera.Parameters parameters;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        surfaceView = (SurfaceView) findViewById(R.id.camera_view);
        photograph = (Button) findViewById(R.id.photograph);
        recording = (Button) findViewById(R.id.recording);
        switchs = (Button) findViewById(R.id.switchs);

        photograph.setOnClickListener(this);
        recording.setOnClickListener(this);
        switchs.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.photograph:	//拍照
                if (camera == null) {
                    Toast.makeText(this, "请打开相机", Toast.LENGTH_SHORT).show();
                    return;
                }
                parameters = camera.getParameters();
                parameters.setPictureFormat(ImageFormat.JPEG);
                camera.setParameters(parameters);
                camera.startPreview();
                camera.takePicture(null, null, new Camera.PictureCallback() {
                    @Override
                    public void onPictureTaken(byte[] data, Camera camera) {
                        MyAsyncTask myTask = new MyAsyncTask(MainActivity.this, cameraInfo.facing, data,camera);
                        myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                    }
                });

                break;
            case R.id.recording:

                break;
            case R.id.switchs:

                break;
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            int[] a = new int[2];
            surfaceView.getLocationOnScreen(a);
            shortSize = surfaceView.getHeight();     //横屏为宽度,竖屏为高度
            longSize = surfaceView.getWidth();
            rotation = getWindowManager().getDefaultDisplay().getRotation();
            cameraInfo = new Camera.CameraInfo();
            checkCameraPermission();
        }
    }
    // 1.申请相机权限---------------------------------------------------------------------------
    private void checkCameraPermission() {
        //检查是否有相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            //没权限,请求权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    1);
        } else {
            //有权限
            Log.d("MainActivity", "有权限");
            getCameras(0);  //0为后摄,1为前摄
        }
    }

    //权限请求回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults != null && grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //用户同意权限
                    getCameras(0);  //0为后摄,1为前摄
                } else {
                    // 权限被用户拒绝了,可以提示用户,关闭界面等等。
                    Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
    
    //预览数据监听
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
		camera.addCallbackBuffer(data);
    }

 /*
3.查找指定摄像头ID,根据摄像头ID打开指定的相机----------------------------------------------------------------------------------------------
    根据摄像头的朝向查找指定摄像头id
    0代表后摄的朝向,但是它不一定是后摄的id
    1代表前摄的朝向
*/
    public void getCameras(int lens_facing) {
        //先释放相机资源
        releaseCamera();
        //获取相机信息
        if (cameraInfo == null) {
            cameraInfo = new Camera.CameraInfo();
        }
        //获取相机个数
        int cameraCount = Camera.getNumberOfCameras();
        //由于不知道第几个是前置摄像头,遍历获取前置摄像头
        for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);

            if (cameraInfo.facing == lens_facing) {
                //根据id打开相机
                Log.d("MainActivity", "相机id为" + camIdx);
                camera = Camera.open(camIdx);
                setPreview();
                break;
            }
        }
    }

    /*
    4.设置预览参数和相机参数后开启预览-----------------------------------------------------------
     */
    public void setPreview() {
        try {
            //设置摄像头参数
            parameters = camera.getParameters();
            //设置预览大小
            parameters.setPreviewSize(1920, 1080);
            //设置图片大小,拍照
            parameters.setPictureSize(1920, 1080);
            //设置预览格式,所有的相机都支持 NV21格式
            parameters.setPreviewFormat(ImageFormat.NV21);
            //设置聚焦
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            //修改相机摄像头参数
            camera.setParameters(parameters);
            //修改相机预览方向
            camera.setDisplayOrientation(adjustCameraOrientation());
            //绑定预览视图
            camera.setPreviewDisplay(surfaceView.getHolder());
            //设置预览数据监听
            camera.setPreviewCallbackWithBuffer(this);
            //开始预览
            camera.startPreview();
        } catch (IOException e) {
        }
    }
     //获取预览最佳尺寸
    private Camera.Size getBestSize() {
        List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
        Camera.Size bestSize = null;
        float uiRatio = (float) longSize / shortSize;
        float minRatio = uiRatio;
        for (Camera.Size previewSize : sizes) {
            float cameraRatio = (float) previewSize.width / previewSize.height;

            //如果找不到比例相同的,找一个最近的,防止预览变形
            float offset = Math.abs(cameraRatio - minRatio);
            if (offset < minRatio) {
                minRatio = offset;
                bestSize = previewSize;
            }
            //比例相同
            if (uiRatio == cameraRatio) {
                bestSize = previewSize;
                break;
            }
        }
        return bestSize;
    }

    /**
     * 调整预览方向
     * 由于手机的图片数据都来自摄像头硬件传感器,这个传感器默认的方向横向的,所以要根据前后摄像头调整方向
     */
    private int adjustCameraOrientation() {
        //判断当前的横竖屏

        int degress = 0;
        //获取手机的方向
        switch (rotation) {
            case Surface.ROTATION_0:
                degress = 0;
                break;
            case Surface.ROTATION_90:
                degress = 90;
                break;
            case Surface.ROTATION_180:
                degress = 180;
                break;
            case Surface.ROTATION_270:
                degress = 270;
                break;
        }
        int result = 0;
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            //后置摄像头
            result = (cameraInfo.orientation - degress + 360) % 360;
        } else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            //前置摄像头,多一步镜像
            result = (cameraInfo.orientation + degress) % 360;
            result = (360 - result) % 360;
        }
        return result;
    }
    //释放相机资源
    private void releaseCamera() {
        if (camera != null) {
            camera.stopPreview();
            camera.stopFaceDetection();
            camera.setPreviewCallback(null);
            camera.release();
            camera = null;
        }
    }
}

MyAsyncTask.java

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class MyAsyncTask extends AsyncTask<Void, Void, File> {
    Context ct;
    int facing;
    byte[] data;
    Camera camera;

    public MyAsyncTask(Context ct, int facing, byte[] data, Camera camera) {
        this.ct = ct;
        this.facing = facing;
        this.data = data;
        this.camera = camera;
    }
    @Override
    protected File doInBackground(Void... voids) {
        File file = null;
        //处理图片数据
        Bitmap bitmap= BitmapFactory.decodeByteArray(data, 0, data.length);
        if (facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            Matrix matrix = new Matrix();
            matrix.postRotate(180);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        }
        //创建图片文件
        try{
            String filePath=null;
            if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                if(Build.VERSION.SDK_INT< 29){
                    filePath=Environment.getExternalStorageDirectory().getCanonicalPath();
                }else{
                    filePath=ct.getExternalFilesDir(null).getAbsolutePath();
                }
            }
            file = new File(filePath, System.currentTimeMillis() + ".jpg");
         //将图片数据写入图片文件
            FileOutputStream outStream=new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,outStream);
            outStream.flush();
            outStream.close();
        }catch(IOException e){
            e.printStackTrace();
        }
        return file;
    }

    @Override
    protected void onPostExecute(File file) {
        super.onPostExecute(file);
        if (file != null) {
            Toast.makeText(ct, "图片保存成功", Toast.LENGTH_SHORT).show();
            camera.startPreview();
        } else {
            Toast.makeText(ct, "图片保存失败", Toast.LENGTH_SHORT).show();
        }
    }
}


总结

本片内容比较粗浅,没有深入,主要是我理解的还不够,但是基本的步骤已经很清晰了,能保证基本的拍照没问题,至于拍出来保存的是个什么样子就不敢保证了,有空再研究

你可能感兴趣的:(Android,android,android,studio)