Camera2实现预览、拍照、摄像机切换

首先布局文件里创建一个TextureView用于显示,一个ImageButton用于拍照,一个ImageButton用于切换

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextureView
        android:id="@+id/textureview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:layout_alignParentBottom="true">
        <ImageButton
            android:id="@+id/takepicture_button"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_centerInParent="true" 
            android:visibility="visible"/>
        <ImageButton
            android:id="@+id/switch_buttont"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginRight="20dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"     
            android:visibility="visible"/>
    </RelativeLayout>
</RelativeLayout>

1.全局变量

 private TextureView textureView;
    private String cameraId = String.valueOf(CameraCharacteristics.LENS_FACING_FRONT);//摄像头id
    private Size previewSize;//预览尺寸
    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    //键值对存储的容器
    // 摄像头状态回调
    private CameraDevice.StateCallback stateCallback;
    private CameraDevice cameraDevice;
    private ImageReader imageReader;
    private Surface previewsurface;
    private CaptureRequest.Builder previewRequestBuilder;
    private CameraCaptureSession cameraCaptureSession;
    private CaptureRequest previewRequest;
    private ImageButton tekepicture_button;
    private ImageButton vidiorecode_button;
    private static final SparseIntArray ORIENTATION = new SparseIntArray();
    private static final String[] permissions = new String[]{
            Manifest.permission.CAMERA,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.RECORD_AUDIO};
    private List<String> permissionList = new ArrayList<>();
    private ImageButton switch_button;

2.加载控件,设置监听

protected void onCreate(Bundle savedInstanceState) {
        Log.d("--ActivityLife--", "onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textureView = (TextureView) findViewById(R.id.textureview);
        tekepicture_button = (ImageButton) findViewById(R.id.takepicture_button);
        tekepicture_button.setOnClickListener(MainActivity.this);
        switch_button = (ImageButton) findViewById(R.id.switch_buttont);
        switch_button.setOnClickListener(MainActivity.this);  
        permissionApply();//获取权限
        hideActionBar();//隐藏活动标题栏导航栏
    }
@Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.takepicture_button:
                capture();
                break;
            case R.id.switch_buttont:
                switchCamera();
                break;
        }
    }
 //onResume里重新打开摄像机:解决活动在暂停后再次打开时停止的问题
    @Override
    protected void onResume() {
        super.onResume();
        if (textureView.isAvailable()) {      
           openCamera();
        } else {
            setTextureViewListener();
        }
    }

3.权限申请(三个权限)

private void permissionApply() {
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(MainActivity.this, permission) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(permission);
            }
        }
        if (!permissionList.isEmpty()) {
            ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), 1000);
        }
    }

权限申请的回调函数

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == 1000) {
            //权限请求失败
            if (grantResults.length > 0) {
                boolean flag = textureView.isAvailable();
                // Log.d("TEXTURE--", String.valueOf(flag));
                //存放没授权的权限
                List<String> deniedPermissions = new ArrayList<>();
                for (int i = 0; i < grantResults.length; i++) {
                    int grantResult = grantResults[i];
                    String permission = permissions[i];
                    if (grantResult != PackageManager.PERMISSION_GRANTED) {
                        deniedPermissions.add(permission);
                    }
                }
                //如果存在没有申请过的权限,进行权限申请
                if (!deniedPermissions.isEmpty()) {
                    Toast.makeText(MainActivity.this, "NOT GRANTED PERMISSION", Toast.LENGTH_SHORT).show();
                    finish();
                }
            }
        }
    }

4.为Texture设置监听

//为textureView设置一个监听,当onSurfaceTextureAvailable即Texture可用的时候
// 获取预览尺寸checkCamera(),
// 配置预览尺寸configureTransform(),
// 打开相机
    private void setTextureViewListener() {
        //textureView设置一个监听
        textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            //在textureview可用的时候
            @Override
            public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int width, int height) {
                checkCamera(width, height);//获取预览尺寸
                configureTransform(width, height);//把矩阵添加到TextureView,配置预览尺寸
                openCamera();//打开相机            
            }

            @Override
            public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {

            }
        });
    }

5.checkCamera()将设备可用的尺寸输出,寻找一个适合的预览尺寸:遍历相机摄像头,

  • cameraManager.getCameraIdList()方法能获取设备的所有相机id列表,
  • 通过cameraManager.getCameraCharacteristics(id)方法获取相机的信息提供者CameraCharacteristics,CameraCharacteristics是一个只读的相机信息提供者,其内部携带大量的相机信息,包括代表相机朝向的 LENS_FACING; 判断闪光灯是否可用的FLASH_INFO_AVAILABLE; 获取所有可用 AE 模式的 CONTROL_AE_AVAILABLE_MODES 等等;
  • 通过cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)获取map;
  • 通过map.getOutPutSizes()方法获取预览的所有尺寸列表;该方法会要求你传递一个 Class 类型,然后根据这个类型返回对应的尺寸列表,如果给定的类型不支持,则返回 null, ImageReader:常用来拍照或接收 YUV数据,MediaRecorder:常用来录制视频,MediaCodec:常用来录制视频,SurfaceHolder:常用来显示预览画面,SurfaceTexture:常用来显示预览画面。宽是长边,高是短边;
    -通过自定义的getOptimalSize()方法进行尺寸的匹配,获取最适合的尺寸
 private void checkCamera(int width, int height) {
        //获取摄像头的管理者
        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            //遍历摄像头
            for (String id : cameraManager.getCameraIdList()) {
                // 默认打开后置摄像头 - 忽略前置摄像头
                if (!cameraId.equals(id)) continue;
                // CameraCharacteristics 是一个只读的相机信息提供者,
                CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
                //通过 CameraCharacteristics 获取该设备支持的所有预览尺寸
                // (先通过 SCALER_STREAM_CONFIGURATION_MAP 获取 StreamConfigurationMap 对象,
                // 然后通过 StreamConfigurationMap.getOutputSizes() 方法获取尺寸列表
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                //Camera2 则是把尺寸信息设置给 Surface,例如接收预览画面的 SurfaceTexture,或者是接收拍照图片的 ImageReader,
                // 相机在输出图像数据的时候会根据 Surface 配置的 Buffer 大小输出对应尺寸的画面。
                backOrientation();
                //对ORIENTATION赋值,默认为后置,因此旋转90
                previewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                //获取TextureView的尺寸配置
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
 // 选择sizeMap中大于并且最接近width和height的size
    private Size getOptimalSize(Size[] sizeMap, int width, int height) {
        List<Size> sizeList = new ArrayList<>();
        for (Size option : sizeMap) {
            if (width > height) {
                if (option.getWidth() > width && option.getHeight() > height) {
                    sizeList.add(option);
                }
            } else {
                if (option.getWidth() > height && option.getHeight() > width) {
                    sizeList.add(option);
                }
            }
        }
        if (sizeList.size() > 0) {
            return Collections.min(sizeList, new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
                }
            });
        }
        return sizeMap[0];
    }

6.configureTransform(width, height)把矩阵添加到TextureView,配置预览尺寸(TextureView和刚刚得到的最适合的尺寸)

 private void configureTransform(int viewWidth, int viewHeight) {
        if (null == textureView || null == previewSize) {
            return;
        }
        //角度
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        Matrix matrix = new Matrix();
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
        RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
        float centerX = viewRect.centerX();
        float centerY = viewRect.centerY();
        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
            float scale = Math.max(
                    (float) viewHeight / previewSize.getHeight(),
                    (float) viewWidth / previewSize.getWidth());
            matrix.postScale(scale, scale, centerX, centerY);
            matrix.postRotate(90 * (rotation - 2), centerX, centerY);
        } else if (Surface.ROTATION_180 == rotation) {
            matrix.postRotate(180, centerX, centerY);
        }
        textureView.setTransform(matrix);
    }

7.openCamera();//打开相机

  • CameraManager cameraManager=getSystemService(Context.CAMERA_SERVICE);
  • 确定是否打开了相机权限
  • 利用cameraManager.openCamera()方法打开相机
  • 由于openCamera()方法需要回调函数,因此补充新建一个stateCallback,在方法里的onOpened()中获取打开的设备cameraDevices并打开预览startPreview()
private void openCamera() {
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    //打开相机,第一个参数指示打开哪个摄像头,
    // 第二个参数stateCallback为相机的状态回调接口,
    // 第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行
    stateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevices) {
            cameraDevice = cameraDevices;
            startPreview();//开始预览
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int i) {
        }
    };
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        finish();
    }
    try {
        cameraManager.openCamera(cameraId, stateCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

8.startPreView()开始预览

  • 创建imageReader,configImageReader(); ImageReader.newInstance()

  • 新建imageReader对象,前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几张;

  • 给imageReader设置监听:imagerReader.setOnImageAvailableListener(),监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable方法,它的参数就是预览帧数据,可以对这帧数据进行处理;

  • 监听者:new ImageReader.OnImageAvailableListener的回调函数nImageAvailable():获取生成的图片image,开启存储图片的线程ImageSave(线程在拍照后才会存储图片,线程的代码在拍照后面),定义一个intent用于发送广播通知系统更新相册。

private void configImageReader() {
    //前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几帧数据, Image 对象池的大小
    imageReader = ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), ImageFormat.JPEG, 1);
    //监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable方法,它的参数就是预览帧数据,可以对这帧数据进行处理
    //当有图像数据生成的时候,ImageReader 会通过通过 ImageReader.OnImageAvailableListener.onImageAvailable() 方法通知我们
    imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader imageReader) {
            Image image = imageReader.acquireLatestImage();//获取生成的图片
            // if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!=)
            new Thread(new ImageSave(image)).start();// 保存图片
        }
    }, null);
}
  • 创建Surface对象先通过TextureView.getSurfaceTexture()方法获取SurfaceTexture对象,设置缓冲区大小,并传入SurfaceTexture创建Surface对象;

  • 发出预览请求,通过cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)请求,获得一个CaptureRequest.Builder对象,CmeraDevices.TEMPLATE_PREVIEW常量为预览请求的常量,后面拍照传入的常量换为CmeraDevices.TEMPLATE_STILL_CAPTURE就可以,通过Builder将surface设置,并build()获取CaptureRequest对象;

  • 通过cameraDevice.createCaptureSession(Array.asList(previewsurface,imageReader.getSurface()),sessionStateCallback,null)创建捕捉会话,补充回调函数CameraCaptureSession.StateCallback对象,会话创建成功后获取会话, repeatPreview()开启重复预览;

//开始预览
    private void startPreview() {
        //创建imageReager,设置监听
        configImageReader();
        //设置TextureView的缓冲区大小并且创建 Surface 对象
        SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
        surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
        previewsurface = new Surface(surfaceTexture);
        //发出预览请求
        try {
            Log.d("OPENCAMEREA--","STARTPREVIEW()");
            previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        //设置预览的显示界面
        previewRequestBuilder.addTarget(previewsurface);
        previewRequest = previewRequestBuilder.build();//请求
        //创建相机捕获会话,
        // 第一个参数是捕获数据的输出Surface列表,
        // 第二个参数用于监听 Session 状态的CameraCaptureSession.StateCallback对象,
        // (是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,)
        // 第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行,
        // (用于执行 CameraCaptureSession.StateCallback 的 Handler 对象,可以是异步线程的 Handler,也可以是主线程的 Handler)
        try {
            CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession CaptureSession) {
                    cameraCaptureSession = CaptureSession;//会话
                    repeatPreview();//重复预览
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {

                }
            };
            cameraDevice.createCaptureSession(Arrays.asList(previewsurface, imageReader.getSurface()), sessionStateCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

通过cameraCaptureSession.setRepeatingRequest()开启,补充回调函数CameraCaptureSession.CaptureCallback previewCaptureCallback;

 //重复预览
    private void repeatPreview() {
        CameraCaptureSession.CaptureCallback previewCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        };
        try {
            cameraCaptureSession.setRepeatingRequest(previewRequest, previewCaptureCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

9.拍照

  • 创建捕获请求,依旧利用cameraDevice.creatCaptureRequest()方法,传入的常量换为CmeraDevices.TEMPLATE_STILL_CAPTURE,
    获取屏幕的方向rotation=getWindowManager().getDefaultDisplay().getRotation(),将Surface对象和imageReader传入,对Builder进行配置,停止预览,通过会话cameraCaptureSession的capture捕捉,参数为一个通过build()方法创建的CaptureRequest对象,回调,null,补充回调函数,重新开始重复预览repeatPreview();
private void capture() {
    try {
        //首先我们创建请求拍照的CaptureRequest
        final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);

        //获取屏幕方向
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        //一个 CaptureRequest 除了需要配置很多参数之外,还要求至少配置一个 Surface(任何相机操作的本质都是为了捕获图像),
        captureBuilder.addTarget(previewsurface);
        captureBuilder.addTarget(imageReader.getSurface());

        captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));

        cameraCaptureSession.stopRepeating();
        CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                Toast.makeText(MainActivity.this, "图片已保存", Toast.LENGTH_SHORT).show();
                repeatPreview();
            }
        };
        cameraCaptureSession.capture(captureBuilder.build(), captureCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

拍照捕获后保存图片的线程ImageSave

  • 获取图片,由于 在 Image 对象里图像数据根据不同格式被划分多个部分分别存储在单独的 Plane 对象里,要先通过调用
    Image.getPlanes() 方法获取所有的 Plane 对象的数组,最后通过 Plane.getBuffer() 获取每一个
    Plane 里存储的图像数据。ByteBuffer buffer=image.getPlanes()[0].getBuffer();

  • 获取图像数据,利用byte的数组存储,由于在前置摄像的图片会呈现镜面,因此创建Bitmap对象,用以将镜像翻转,BitmapFactory.decodeByteArray(data,0,datda.length)创建对象,创建File对象用以存储照片,利用FileOutPut,并通过Bitmap.compress()写入,

  • 释放资源,当我们不再需要使用某一个Image 对象的时候记得通过该方法释放资源,因为 Image 对象实际上来自于一个创建
    ImageReader时就确定大小的对象池,如果我们不释放它的话就会导致对象池很快就被耗光,并且抛出一个异常。

public class ImageSave implements Runnable {
        private Image image;

        public ImageSave(Image getimage) {
            image = getimage;
        }

        @Override
        public void run() {
            //在 Image 对象里图像数据根据不同格式被划分多个部分分别存储在单独的 Plane 对象里,
            //通过调用 Image.getPlanes() 方法获取所有的 Plane 对象的数组
            //最后通过 Plane.getBuffer() 获取每一个 Plane 里存储的图像数据。
            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
            byte[] data = new byte[buffer.remaining()];//remaining()返回剩余的可用长度
            buffer.get(data);

            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            if ("1".equals(cameraId)) {
                Matrix m = new Matrix();
                m.postScale(-1, 1); // 镜像水平翻转
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
            }

            // File imageFile = new File(getExternalCacheDir(),System.currentTimeMillis()+".jpg");
            //String path = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/DCIM";
            File imageFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)+"/Camera", System.currentTimeMillis() + ".jpg");
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(imageFile);
                //  fileOutputStream.write(data, 0, data.length);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);

                String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera";
                Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                Uri uri = Uri.fromFile(new File(path));
                intent.setData(uri);
                sendBroadcast(intent);//发送广播通知系统更新相册  
            } catch (IOException e) {
                e.printStackTrace();
            } finally {

                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                image.close();
                //当我们不再需要使用某一个 Image 对象的时候记得通过该方法释放资源,
                // 因为 Image 对象实际上来自于一个创建 ImageReader 时就确定大小的对象池,
                // 如果我们不释放它的话就会导致对象池很快就被耗光,并且抛出一个异常。
            }

        }
    }

10.前后摄像头转换

  • 前后摄像的角度旋转不一样,因此在切换后需要对ORIENTATION更新,前置旋转270,后置90
 //前置拍摄时,照片旋转270
    private void frontOrientation() {
        //前置时,照片旋转270
        ORIENTATION.append(Surface.ROTATION_0, 270);
        ORIENTATION.append(Surface.ROTATION_90, 0);
        ORIENTATION.append(Surface.ROTATION_180, 90);
        ORIENTATION.append(Surface.ROTATION_270, 180);
    }
  //后置拍摄时,照片旋转90
    private static void backOrientation() {
        //后置时,照片旋转90
        ORIENTATION.append(Surface.ROTATION_0, 90);
        ORIENTATION.append(Surface.ROTATION_90, 0);
        ORIENTATION.append(Surface.ROTATION_180, 270);
        ORIENTATION.append(Surface.ROTATION_270, 180);
    }
  • 对当前相机id进行判断和改变,依旧要利用cameraManager.getCameraCharacteristics获取信息提供,利用characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURTION_MAP)获取StreamConfigurationMap
    map
 private void switchCamera() {
        //获取摄像头的管理者
        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            for (String id : cameraManager.getCameraIdList()) {
                CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                previewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), textureView.getWidth(), textureView.getHeight());
                if (cameraId.equals(String.valueOf(CameraCharacteristics.LENS_FACING_BACK)) && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
                    cameraId = String.valueOf(CameraCharacteristics.LENS_FACING_FRONT);
                    cameraDevice.close();
                    backOrientation();
                    openCamera();
                    break;
                } else if (cameraId.equals(String.valueOf(CameraCharacteristics.LENS_FACING_FRONT)) && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) {
                    cameraId = String.valueOf(CameraCharacteristics.LENS_FACING_BACK);
                    cameraDevice.close();
                    frontOrientation();
                    openCamera();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
使用Camera2 Api实现相机预览需要以下步骤: 1. 实现CameraDevice.StateCallback接口,当相机设备状态发生变化时会回调该接口。 2. 获取相机管理器,并申请相机权限。 3. 打开相机,并设置预览Surface和相机回调。 4. 获取预览数据,进行相应的渲染和处理。 以下是示例代码: ```java public class CameraPreviewActivity extends AppCompatActivity implements SurfaceHolder.Callback, CameraDevice.StateCallback { private CameraManager mCameraManager; private String mCameraId; private CameraDevice mCameraDevice; private CameraCaptureSession mCaptureSession; private SurfaceHolder mSurfaceHolder; private Size mPreviewSize; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_camera_preview); SurfaceView surfaceView = findViewById(R.id.surface_view); mSurfaceHolder = surfaceView.getHolder(); mSurfaceHolder.addCallback(this); mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { mCameraId = mCameraManager.getCameraIdList()[0]; } catch (CameraAccessException e) { e.printStackTrace(); } if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1); } else { openCamera(); } } private void openCamera() { try { mCameraManager.openCamera(mCameraId, this, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; try { List<Surface> surfaces = new ArrayList<>(); // 设置预览Surface Surface previewSurface = mSurfaceHolder.getSurface(); surfaces.add(previewSurface); // 创建相机会话 mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { mCaptureSession = session; startPreview(); } @Override public void onConfigureFailed(CameraCaptureSession session) { Log.e("CameraPreview", "createCaptureSession failed"); } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } private void startPreview() { try { // 创建预览请求 CaptureRequest.Builder previewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); previewBuilder.addTarget(mSurfaceHolder.getSurface()); //设置自动对焦模式 previewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 开始预览 mCaptureSession.setRepeatingRequest(previewBuilder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onSurfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 获取预览大小 try { CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(mCameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); mPreviewSize = map.getOutputSizes(SurfaceHolder.class)[0]; // 更新Surface大小 int rotatedPreviewWidth = width; int rotatedPreviewHeight = height; int maxWidth = mSurfaceHolder.getSurfaceFrame().width(); int maxHeight = mSurfaceHolder.getSurfaceFrame().height(); if (maxWidth > 0 && maxHeight > 0) { rotatedPreviewWidth = Math.min(maxWidth, maxHeight); rotatedPreviewHeight = Math.max(maxWidth, maxHeight); } mSurfaceHolder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { mCameraDevice.close(); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == 1) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { openCamera(); } else { Toast.makeText(this, "Camera permission not granted", Toast.LENGTH_SHORT).show(); } } } @Override public void onDisconnected(CameraDevice camera) { mCameraDevice.close(); } @Override public void onError(CameraDevice camera, int error) { mCameraDevice.close(); mCameraDevice = null; } } ``` 在AndroidManifest.xml中添加相机权限: ```xml <uses-feature android:name="android.hardware.camera" /> <uses-permission android:name="android.permission.CAMERA" /> ``` 在布局文件中添加SurfaceView: ```xml <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值