美文网首页音视频Android应用开发那些事
Android相机<第五篇>:SurfaceView使用

Android相机<第五篇>:SurfaceView使用

作者: NoBugException | 来源:发表于2019-09-16 17:27 被阅读0次

    这一篇文章其实和上一篇文章类似,但是好处在于已经解决了上一篇文章结尾处提到的OPPO手机的问题。本文就不过多的文字描述了,只有两个部分,第一部分说一下SurfaceView监听回调方法的意思,第二部分就直接粘贴代码了。

    监听

    预览页面需要实现的监听的接口如下:

    public interface Callback {
        /**
         * This is called immediately after the surface is first created.
         * Implementations of this should start up whatever rendering code
         * they desire.  Note that only one thread can ever draw into
         * a {@link Surface}, so you should not draw into the Surface here
         * if your normal rendering will be in another thread.
         *
         * @param holder The SurfaceHolder whose surface is being created.
         */
        public void surfaceCreated(SurfaceHolder holder);
    
        /**
         * This is called immediately after any structural changes (format or
         * size) have been made to the surface.  You should at this point update
         * the imagery in the surface.  This method is always called at least
         * once, after {@link #surfaceCreated}.
         *
         * @param holder The SurfaceHolder whose surface has changed.
         * @param format The new PixelFormat of the surface.
         * @param width The new width of the surface.
         * @param height The new height of the surface.
         */
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                   int height);
    
        /**
         * This is called immediately before a surface is being destroyed. After
         * returning from this call, you should no longer try to access this
         * surface.  If you have a rendering thread that directly accesses
         * the surface, you must ensure that thread is no longer touching the
         * Surface before returning from this function.
         *
         * @param holder The SurfaceHolder whose surface is being destroyed.
         */
        public void surfaceDestroyed(SurfaceHolder holder);
    }
    
    • surfaceCreated:每次初始化预览界面时,都会执行一次这个方法,用于初始化相机资源。

    • surfaceChanged:当预览界面布局发生改变时执行一次,与TextureViewonSurfaceTextureSizeChanged一样。但是区别是:SurfaceView在初始化时就会执行surfaceChanged方法,但是TextureView在初始化时不会执行onSurfaceTextureSizeChanged

    • surfaceDestroyed:当预览界面切入后台或者Activity销毁时执行。

    实现代码

    预览界面类

    public class CameraPreview2 extends SurfaceView implements SurfaceHolder.Callback {
    
    
        private Camera mCamera;
    
        public CameraPreview2(Context context) {
            super(context);
        }
    
        /**
         * 获取相机对象
         * @return
         */
        public Camera getCamera(){
            return mCamera;
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
    
            try {
                mCamera = Camera.open();
    
                mCamera.enableShutterSound(false);
    
                //设置预览方向,预览方向默认是横屏的,所以这里要调整一下预览方向
                mCamera.setDisplayOrientation(90);
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                //获取最适合的分辨率
                Camera.Size previewSize = getCameraSize(parameters.getSupportedPreviewSizes(), holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
                parameters.setPreviewSize(previewSize.width, previewSize.height);
                //获取最适合的分辨率
                Camera.Size pictureSize = getCameraSize(parameters.getSupportedPictureSizes(), holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
                parameters.setPictureSize(pictureSize.width, pictureSize.height);
    
                //设置拍照之后,本地图片格式
                parameters.setPictureFormat(ImageFormat.JPEG);
                //拍照后的图片文件旋转90度
                parameters.setRotation(90);
                mCamera.setParameters(parameters);
                //将相机的预览效果显示出来
                mCamera.setPreviewDisplay(holder);
    
                //开启预览
                mCamera.startPreview();
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取最合适的Size
         * @param sizeList
         * @param width
         * @param height
         * @return
         */
        private Camera.Size getCameraSize(List<Camera.Size> sizeList, int width, int height){
    
            Camera.Size tempSize = null;
            float aspectRatio = height * 1.0f / width;//求出预期横宽比
            float offset = aspectRatio;//预期横宽比和实际横宽比误差
    
            for(Camera.Size size : sizeList){
    
                if(size.width < height || size.height < width){
                    continue;
                }
    
                //误差最小值
                if(Math.abs(aspectRatio - size.width * 1.0f / size.height) < offset){
                    offset = Math.abs(aspectRatio - size.width * 1.0f / size.height);
                    tempSize = size;
                }
            }
    
            if(tempSize == null){
                tempSize = sizeList.get(0);
            }
    
    
            return tempSize;
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    
    
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
    
            if(mCamera != null){
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
            }
        }
    }
    

    在Activity中的运用

    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private CameraPreview2 mPreview;
        private FrameLayout fl_camera_preview;
        private ImageView takePhone;
        private ImageView flash_button;
        //是否开启闪光灯
        private boolean isFlashing;
    
        //图片临时缓存
        private byte[] imageData;
    
        private ImageView save_button;
        private ImageView cancle_save_button;
        private Button cancle_button;
    
        private RelativeLayout ll_photo_layout;
        private RelativeLayout ll_confirm_layout;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
    
            //全屏
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
            setContentView(R.layout.activity_main);
    
            fl_camera_preview = findViewById(R.id.camera_preview);
            takePhone = findViewById(R.id.take_photo_button);
            takePhone.setOnClickListener(this);
            flash_button = findViewById(R.id.flash_button);
            flash_button.setOnClickListener(this);
            ll_photo_layout = findViewById(R.id.ll_photo_layout);
            ll_confirm_layout = findViewById(R.id.ll_confirm_layout);
            save_button = findViewById(R.id.save_button);
            save_button.setOnClickListener(this);
            cancle_save_button = findViewById(R.id.cancle_save_button);
            cancle_save_button.setOnClickListener(this);
            cancle_button = findViewById(R.id.cancle_button);
            cancle_button.setOnClickListener(this);
    
    
            // 创建预览界面
            mPreview = new CameraPreview2(this);
            //添加监听
            mPreview.getHolder().addCallback(mPreview);
            //将预览页面添加到FrameLayout中
            fl_camera_preview.addView(mPreview);
    
        }
    
        /**
         * 调用摄像头开始拍照片
         */
        private void takePhoto() {
            //调用相机拍照
    
            final Camera mCamera = mPreview.getCamera();
    
            if(mCamera == null){
                return;
            }
    
            //拍照
            mCamera.takePicture(null, null, null, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    //视图动画
                    ll_photo_layout.setVisibility(View.GONE);
    
                    ObjectAnimator anim1 = ObjectAnimator.ofFloat(ll_confirm_layout, "scaleX", 0, 1.0f);
                    ObjectAnimator anim2 = ObjectAnimator.ofFloat(ll_confirm_layout, "scaleY", 0, 1.0f);
    
                    AnimatorSet animatorSet = new AnimatorSet();
                    animatorSet.setDuration(1000);
                    animatorSet.play(anim1).with(anim2);
                    animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
                    animatorSet.start();
    
                    ll_confirm_layout.setVisibility(View.VISIBLE);
    
                    //停止预览
                    mCamera.stopPreview();
    
                    imageData = data;
                }
            });
        }
    
    
        /**
         * 保存照片
         * @param imageData
         */
        private void savePhoto(byte[] imageData) {
            if(imageData.length == 0){
                return;
            }
            FileOutputStream fos = null;
            String cameraPath = Environment.getExternalStorageDirectory().getPath() + File.separator + "1";
            //相册文件夹
            File cameraFolder = new File(cameraPath);
            if (!cameraFolder.exists()) {
                cameraFolder.mkdirs();
            }
            //保存的图片文件
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
            String imagePath = cameraFolder.getAbsolutePath() + File.separator + "IMG_" + simpleDateFormat.format(new Date()) + ".jpg";
            File imageFile = new File(imagePath);
            try {
                fos = new FileOutputStream(imageFile);
                fos.write(imageData);
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    
        /**
         * 切换闪光灯
         */
        private void switchFlash() {
    
            Camera mCamera = mPreview.getCamera();
    
            if(mCamera == null){
                return;
            }
    
            isFlashing = !isFlashing;
            flash_button.setImageResource(isFlashing ? R.mipmap.flash_open : R.mipmap.flash_close);
    
            try {
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setFlashMode(isFlashing ? Camera.Parameters.FLASH_MODE_TORCH : Camera.Parameters.FLASH_MODE_OFF);
                mCamera.setParameters(parameters);
            } catch (Exception e) {
                Toast.makeText(this, "该设备不支持闪光灯", Toast.LENGTH_SHORT);
            }
        }
    
    
        /**
         * 快速切换到预览状态
         */
        private void changePreview() {
    
            Camera mCamera = mPreview.getCamera();
    
            if(mCamera == null){
                return;
            }
    
            ll_confirm_layout.setVisibility(View.GONE);
    
            ObjectAnimator anim1 = ObjectAnimator.ofFloat(ll_photo_layout, "scaleX", 0, 1.0f);
            ObjectAnimator anim2 = ObjectAnimator.ofFloat(ll_photo_layout, "scaleY", 0, 1.0f);
    
            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.setDuration(1000);
            animatorSet.play(anim1).with(anim2);
            animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
            animatorSet.start();
    
            ll_photo_layout.setVisibility(View.VISIBLE);
    
            //开始预览
            mCamera.startPreview();
            imageData = null;
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.take_photo_button:
                    //拍照
                    takePhoto();
                    break;
                case R.id.flash_button:
                    //切换闪光灯
                    switchFlash();
                    break;
                case R.id.save_button:
                    //保存照片
                    savePhoto(imageData);
                    //切换到预览状态
                    changePreview();
                    break;
                case R.id.cancle_save_button:
                    //切换到预览状态
                    changePreview();
                    break;
                case R.id.cancle_button:
                    finish();
                    break;
            }
        }
    }
    

    一些图片和布局文件和上一篇一样,在上一篇拿即可。

    [本章完...]

    相关文章

      网友评论

        本文标题:Android相机<第五篇>:SurfaceView使用

        本文链接:https://www.haomeiwen.com/subject/pflryctx.html