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












更多相关文章

  1. Android蓝牙开发中电话音频(HSP,HFP)和媒体音频(A2DP,AVRCP)到底是个什
  2. Android中使用官方提供好的功能使用说明(比如系统图库获取),也作
  3. [置顶] 了解Android微信里的WebView是如何实现分享的功能
  4. Android蓝牙开发中电话音频(HSP,HFP)和媒体音频(A2DP,AVRCP)到底是个什
  5. Android之使用SoundPool播放一小段音频,实现猜歌的功能
  6. android 借助AccessibilityService实现模拟点击功能-循环一个列
  7. Android(安卓)插件框架 xCombine 开发思路简介
  8. 将你的老旧Android平板或手机改造成服务器
  9. [置顶] Android(安卓)ListView点击之后保持更换的背景色,实现已读

随机推荐

  1. android 快速开发资料查询(陆续更新)
  2. google maps api 地址
  3. android6.0 状态栏添加图标 举例Location
  4. Android(安卓)stuido 快捷键
  5. Android(安卓)Studio编译时Gradle报乱码
  6. android默认debug.keystore的密码
  7. android平台开发问题小结----今天遇到的
  8. android中json解析及使用 (下)
  9. Android(安卓)蓝牙开发浅析
  10. 关于android 经典蓝牙开发 使用UUID连接