音视频Android应用开发那些事

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

2019-09-16  本文已影响0人  NoBugException

这一篇文章其实和上一篇文章类似,但是好处在于已经解决了上一篇文章结尾处提到的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);
}
实现代码

预览界面类

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;
        }
    }
}

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

[本章完...]

上一篇 下一篇

猜你喜欢

热点阅读