Android开发 之 Camera2之拍照

Camera2之拍照

Android框架包括支持各种相机和相机的特性 设备,允许您捕获照片和视频在您的应用程序。 本文讨论了 快速、简单的图像和视频捕获方法,并概述了一个先进的方法来创建 为用户自定义相机的经验。

1.在清单文件中声明
权限
如果保存照片,录视频还要添加两个权限
    
    


相机功能:相机的特性,例如
    
    
还有很多如下:
android.hardware.camera
应用使用设备的后置相机。只有前置相机的设备不会列出该功能,因此如果您的应用可与任何朝向的相机通信,请改用 android.hardware.camera.any 功能。
android.hardware.camera.any
应用使用设备的其中一个相机或用户为设备连接的外置相机。 如果您的应用不要求相机必须是后置式,请使用此值来替代 android.hardware.camera。
android.hardware.camera.autofocus
应用使用设备相机支持的自动对焦功能。

应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false"。

android.hardware.camera.capability.manual_post_processing
应用使用设备相机支持的 MANUAL_POST_PROCESSING 功能。

您的应用可以通过该功能替换相机的自动白平衡功能。 使用 android.colorCorrection.transform、android.colorCorrection.gains 以及 TRANSFORM_MATRIX 的 android.colorCorrection.mode。

android.hardware.camera.capability.manual_sensor
应用使用设备相机支持的 MANUAL_SENSOR 功能。

该功能隐含对自动曝光锁定 (android.control.aeLock) 的支持,该支持可以让相机的曝光时间和灵敏度一直固定在特定值。

android.hardware.camera.capability.raw
应用使用设备相机支持的 RAW 功能。

该功能暗示设备可以保存 DNG(原始)文件,并且设备的相机提供您的应用直接处理这些原始图像所需的 DNG 相关元数据。

android.hardware.camera.external
应用与用户为设备连接的外置相机通信。 但该功能不能保证外置相机可供您的应用使用。
android.hardware.camera.flash
应用使用设备相机支持的闪光功能。

应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false"。

android.hardware.camera.front
应用使用设备的前置相机。

应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false"。

android.hardware.camera.level.full
应用使用设备的至少一个相机提供的 FULL 级图像捕捉支持。 提供 FULL 支持的相机可提供快速捕捉功能、逐帧控制和手动后期处理控制。


下面看看具体的代码步骤:
1.本文是利用textureview预览视图数据的。在xml文件中定义textureview组件
创建handler线程,创建handler;
给textureview设置监听
        mPreviewView = (TextureView) findViewById(R.id.textureview);
        mThreadHandler = new HandlerThread("CAMERA2");
        mThreadHandler.start();
        mHandler = new Handler(mThreadHandler.getLooper());
        mPreviewView.setSurfaceTextureListener(surfaceTextureListener);
在textureveiw监听中可以看到以下方法;创建时,尺寸改变时,销毁时,更新时。
我们在textureview初始化的时候创建初始化相机功能,这里先要检察权限,没有权限要去申请权限。
    private TextureView.SurfaceTextureListener surfaceTextureListener=new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
            checkPermission();
        }
        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

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

        }
    };
    public void checkPermission()
    {
        //检查是否有权限
        if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED)
        {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 0);
        }else{
            initCamera();
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 0:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    initCamera();
                }
                break;
        }
    }
2.初始化相机功能
获得相机设备要通过相机服务CameraManager
通过相机id获得相机的参数类CameraCharactoristics对象,这个对象封装了相机相关的支持动能参数,例如支持的分辨率,摄像头旋转角度
通过cameraManager传入相机的id打开相机,设置回调
    public void initCamera()
    {
        cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
        try {
            //获取可用相机设备id列表
            String[] CameraIdList = cameraManager.getCameraIdList();
            //获得前置摄像头的属性对象。
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(CameraIdList[0]);
            //这可用流配置 相机设备支持; 还包括最低帧持续时间 和每个格式的停滞时间/大小组合。
//            characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);

            //分辨率:
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            android.util.Size[] outputSizes = map.getOutputSizes(ImageFormat.JPEG);
            for(android.util.Size size:outputSizes)
            {
                list.add(size.getWidth()+"*"+size.getHeight());
                Log.e("fbl",size.getWidth()+"*"+size.getHeight());
            }
            //获取摄像头的旋转角度
            mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);

            ArrayAdapter arr_adapter= new ArrayAdapter(Camera2Activity.this, R.layout.spinner_item_layout, list);
            arr_adapter.setDropDownViewResource(R.layout.spinner_item_layout);
            spinner.setAdapter(arr_adapter);
//            spinner.setSelection(5);
            //就像这样
            cameraManager.openCamera(CameraIdList[0], mCameraDeviceStateCallback, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
在回调中有打开,失去连接,错误方法的回调
在开发相机回调中设置spinner的监听,对于选择不同的分辨率下创建不同尺寸的ImageReader对象,这个对象是封装了拍照数据的类,设置拍照监听回调后保存照片
开始预览,创建捕捉会议
  /**
     * 相机监听,这个回调不是运行在ui线程的,不能更新UI
     */
    private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            mCaera = camera;
            mFile = new File(Environment.getExternalStorageDirectory(),"TTT.jpg");
            spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView adapterView, View view, int i, long l) {
                    TextView tv= (TextView) view;
                    String  text = (String) tv.getText();
                    String[] split = text.split("\\*");
                    imageReader = ImageReader.newInstance(Integer.parseInt(split[1]), Integer.parseInt(split[0]), ImageFormat.JPEG, /*maxImages*/2);
                    imageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler);

                    ViewGroup.LayoutParams layoutParams = mPreviewView.getLayoutParams();
                    layoutParams.width=min;
                    layoutParams.height=Integer.parseInt(split[0])*min/Integer.parseInt(split[1]);
                    mPreviewView.setLayoutParams(layoutParams);

                    try {
                        if(mSession!=null){
                            mSession.close();
                        }
                        startPreview(mCaera);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }
                @Override
                public void onNothingSelected(AdapterView adapterView) {
                }
            });
        }
        @Override
        public void onDisconnected(CameraDevice camera) {}

        @Override
        public void onError(CameraDevice camera, int error) {}
    };
3.创建捕捉会议,开始预览
获得预览的surface,图片缓存的surface
设置会议监听
  /**
     * 开始预览
     * @param camera
     * @throws CameraAccessException
     */
    private void startPreview(CameraDevice camera) throws CameraAccessException {
        SurfaceTexture texture = mPreviewView.getSurfaceTexture();
        //我们将默认缓冲区的大小配置为我们想要的相机预览的大小。
        texture.setDefaultBufferSize(mPreviewView.getWidth(), mPreviewView.getHeight());
        surface = new Surface(texture);
        //我们设置了一个具有输出Surface的CaptureRequest.Builder。
        try {
            mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewBuilder.addTarget(surface);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        //创建捕捉会议
        camera.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), mSessionStateCallback, mHandler);
    }

会议监听中通过会议发送请求,这只请求监听
    /**
     * 会议状态监听
     */
    private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(CameraCaptureSession session) {
            try {
                //session.capture(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);
                mSession = session;
                //这句是预览的真正代码
                session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(CameraCaptureSession session) {}
    };
    private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback()
    {
        @Override
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result)
        {

        }
        @Override
        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult){

        }
    };

4.通过点击事件触发拍照
创建捕捉请求操作就会在ImageReader的surface中获取一张照片,然后之前设置的拍照监听中去处理图片
 @Override
    public void onClick(View view) {
        switch (view.getId())
        {
            case R.id.bt_cut:
                takePicture();
                break;
            case R.id.imagebutton:
                break;
        }
    }
    public void takePicture(){
        try {
            CaptureRequest.Builder captureRequestBuilder = mCaera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            // 将imageReader的surface作为CaptureRequest.Builder的目标
            captureRequestBuilder.addTarget(imageReader.getSurface());
            // 自动对焦
            captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            // 获取手机方向
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            // 根据设备方向计算设置照片的方向
            captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
            //拍照
            CaptureRequest mCaptureRequest = captureRequestBuilder.build();
            mSession.stopRepeating();
            mSession.capture(mCaptureRequest, null, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
5.处理保存图片
 private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            MyToast.makeText(Camera2Activity.this,"拍照成功");
            mHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
        }
    };
 /**
     * 保存图片
     */
    private static class ImageSaver implements Runnable {
        /**
         * The JPEG image
         */
        private final Image mImage;
        /**
         * The file we save the image into.
         */
        private final File mFile;

        public ImageSaver(Image image, File file) {
            mImage = image;
            mFile = file;
        }

        @Override
        public void run() {
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            FileOutputStream output = null;
            try {
                output = new FileOutputStream(mFile);
                output.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mImage.close();
                if (null != output) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }
6.关闭摄像头,释放资源
@Override
    protected void onPause() {
        closeCamera();
        stopBackgroundThread();
        super.onPause();

    }
    public void closeCamera()
    {
        if(mSession!=null)
        {
            mSession.close();
            mSession=null;
        }
        if (null != mCaera) {
            mCaera.close();//关掉摄像头
            mCaera = null;
        }
    }
    /**
     * Stops the background thread and its {@link Handler}.
     */
    private void stopBackgroundThread() {
        mThreadHandler.quitSafely();//线程安全停止
        try {
            mThreadHandler.join();
            mThreadHandler = null;
            mHandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

总体比5.0以前的相机操作复杂很多,很多回调监听。
本文参考官方提供的demo: https://github.com/googlesamples/android-Camera2Basic













你可能感兴趣的:(Android,5.x,Android,组件,Android,视,图)