Android自定义相机页面

自定义相机页面带裁剪功能(自动获取焦点,解决预览页面和保存图片方向不正确问题)

本文主要记录实际开发需要自定义相机页面功能的实现并根据实际需求自己整理的一套逻辑和代码,如有侵权,请联系删除~

页面大概这个样子,有点丑~~可以根据需求改
Android自定义相机页面_第1张图片
下面是具体的实现方式:

  • 首先建立一个预览的自定义view
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{


    private Camera camera = null;
    private SurfaceHolder surfaceHolder = null;

    public MySurfaceView(Context context, Camera camera) {
        super(context);
        this.camera = camera;
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
    public MySurfaceView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try{
            //开始预览
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
        //根本没有可处理的SurfaceView
        if (surfaceHolder.getSurface() == null){
            return ;
        }

        //先停止Camera的预览
        try{
            camera.stopPreview();
        }catch(Exception e){
            e.printStackTrace();
        }

        //重新开启Camera的预览功能
        try{
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }


}

  • 然后新建一个CameraActivity用于开始预览界面的操作
public class CameraActivity extends AppCompatActivity {
    private ImageView iv_camera_capture = null;
    private Button btn_camera_cancel = null;
    private ImageView iv_camera_flash_light = null;

    private Camera camera = Camera.open();
    private MySurfaceView mySurfaceView = null;

    private byte[] buffer = null;

    private final int TYPE_FILE_IMAGE = 1;
    private final int TYPE_FILE_VEDIO = 2;

    private int openFlashLight = 0;

    public String CUTTING_IMAGE_NAME = "PhotoCopy.jpg";

    private static final int REQUEST_IMAGE_CUTTING = 2;



    private Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            if (data == null) {
                
            } else {
            }

            buffer = new byte[data.length];
            buffer = data.clone();

            //保存图片
            saveImageToFile();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        iv_camera_capture = (ImageView) findViewById(R.id.camera_capture);
        iv_camera_flash_light = (ImageView) findViewById(R.id.camera_flash_light);
        btn_camera_cancel = (Button) findViewById(R.id.camera_cancel);

        iv_camera_capture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                camera.takePicture(null, null, pictureCallback);
            }
        });
        iv_camera_flash_light.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (openFlashLight == 1) {
                    closeFlashLight();
                    openFlashLight = 0;
                    iv_camera_flash_light.setImageResource(R.mipmap.flash_light_open);

                } else if (openFlashLight == 0) {
                    openFlashLight = 1;
                    openFlashLight();
                    iv_camera_flash_light.setImageResource(R.mipmap.flash_light_close);

                }
                
            }
        });
        btn_camera_cancel.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();

        camera.release();
        camera = null;
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (camera == null) {
            camera = getCameraInstance();
        }
        //必须放在onResume中,不然会出现Home键之后,再回到该APP,黑屏
        mySurfaceView = new MySurfaceView(getApplicationContext(), camera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mySurfaceView);

        if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
            camera.setDisplayOrientation(90);
        } else {//如果是横屏
            camera.setDisplayOrientation(0);
        }

        //解决方向问题
        IOrientationEventListener orientationEventListener = new IOrientationEventListener(this);
        mySurfaceView.getHolder().setKeepScreenOn(true);//屏幕常亮

        mySurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            //当SurfaceHolder被创建的时候回调
            @Override
            public void surfaceCreated(SurfaceHolder surfaceHolder) {
                orientationEventListener.enable();

                //设置相机的参数
                Camera.Parameters parameters = camera.getParameters();
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                List sizeList = parameters.getSupportedPreviewSizes();//获取所有支持的camera尺寸
                Camera.Size optionSize = getOptimalPreviewSize(sizeList, mySurfaceView.getHeight(), mySurfaceView.getWidth());//获取一个最为适配的camera.size
                parameters.setPreviewSize(optionSize.width, optionSize.height);//把camera.size赋值到parameters
                camera.setParameters(parameters);
                camera.cancelAutoFocus();
            }

            //当SurfaceHolder的尺寸发生变化的时候被回调
            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

            }

            //当SurfaceHolder被销毁的时候回调
            @Override
            public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
                orientationEventListener.disable();
            }
        });
    }

    /*得到一相机对象*/
    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = camera.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return camera;
    }


    //-----------------------保存图片---------------------------------------
    private void saveImageToFile() {
        File file = getOutFile(TYPE_FILE_IMAGE);
        if (file == null) {
            Toast.makeText(getApplicationContext(), "文件创建失败,请检查SD卡读写权限", Toast.LENGTH_SHORT).show();
            return;
        }
        Log.i("MyPicture", "自定义相机图片路径:" + file.getPath());
        Toast.makeText(getApplicationContext(), "图片保存路径:" + file.getPath(), Toast.LENGTH_SHORT).show();
        if (buffer == null) {
            Log.i("MyPicture", "自定义相机Buffer: null");
        } else {
            try {
                FileOutputStream fos = new FileOutputStream(file);
                fos.write(buffer);
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        startPhotoZoom();
    }

    //-----------------------生成Uri---------------------------------------
    //得到输出文件的URI ***这里的fileProvider为了适配android9 不会的小伙伴自行百度***
    private Uri getOutFileUri(int fileType) {
        return FileProvider.getUriForFile(this, "com.xxx.fileProvider", getOutFile(fileType));
    }

    //生成输出文件
    private File getOutFile(int fileType) {

        String storageState = Environment.getExternalStorageState();
        if (Environment.MEDIA_REMOVED.equals(storageState)) {
            Toast.makeText(getApplicationContext(), "oh,no, SD卡不存在", Toast.LENGTH_SHORT).show();
            return null;
        }

        File mediaStorageDir = new File(Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
                , "MyPictures");
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                Log.i("MyPictures", "创建图片存储路径目录失败");
                Log.i("MyPictures", "mediaStorageDir : " + mediaStorageDir.getPath());
                return null;
            }
        }

        File file = new File(getFilePath(mediaStorageDir, fileType));

        return file;
    }

    //生成输出文件路径
    private String getFilePath(File mediaStorageDir, int fileType) {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                .format(new Date());
        String filePath = mediaStorageDir.getPath() + File.separator;
        if (fileType == TYPE_FILE_IMAGE) {
            filePath += ("IMG_" + timeStamp + ".jpg");
        } else if (fileType == TYPE_FILE_VEDIO) {
            filePath += ("VIDEO_" + timeStamp + ".mp4");
        } else {
            return null;
        }
        return filePath;
    }


    /**
     * 解决预览变形问题
     *
     * @param sizes
     * @param w
     * @param h
     * @return
     */
    private Camera.Size getOptimalPreviewSize(List sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null) return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    public class IOrientationEventListener extends OrientationEventListener {

        public IOrientationEventListener(Context context) {
            super(context);
        }

        @Override
        public void onOrientationChanged(int orientation) {
            if (ORIENTATION_UNKNOWN == orientation) {
                return;
            }
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(0, info);
            orientation = (orientation + 45) / 90 * 90;
            int rotation = 0;
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                rotation = (info.orientation - orientation + 360) % 360;
            } else {
                rotation = (info.orientation + orientation) % 360;
            }
            Log.e("TAG", "orientation: " + orientation);
            if (null != camera) {
                Camera.Parameters parameters = camera.getParameters();
                parameters.setRotation(rotation);
                camera.setParameters(parameters);
            }
        }
    }

    /**
     * 启动系统裁剪
     */
    private void startPhotoZoom() {
        Uri uri = getOutFileUri(TYPE_FILE_IMAGE);

        //保存裁剪后的图片
        File cropFile = new File(Environment.getExternalStorageDirectory().toString() + "/yourname", CUTTING_IMAGE_NAME);
        try {
            if (cropFile.exists()) {
                cropFile.delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Uri cropUri;
        cropUri = Uri.fromFile(cropFile);

        Intent intent = new Intent("com.android.camera.action.CROP");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //添加这一句表示对目标应用临时授权该Uri所代表的文件
            //需要加上这两句话  : uri 权限
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
//        intent.putExtra("aspectX", 1); // 裁剪框比例
//        intent.putExtra("aspectY", 1);
//        intent.putExtra("outputX", 500); // 输出图片大小
//        intent.putExtra("outputY", 500);
        intent.putExtra("scale", true);
        intent.putExtra("return-data", false);

        intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true); // no face detection
        startActivityForResult(intent, REQUEST_IMAGE_CUTTING);
    }

    /**
     * 打开闪光灯
     */
    public void openFlashLight() {
        if (camera == null) {
            return;
        }
        Camera.Parameters parameter = camera.getParameters();
        parameter.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        camera.setParameters(parameter);
    }

    /**
     * 关闭闪光灯
     */
    public void closeFlashLight() {
        if (camera == null) {
            return;
        }
        Camera.Parameters parameter = camera.getParameters();
        parameter.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
        camera.setParameters(parameter);
    }
    //将截取后的照片返回
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_IMAGE_CUTTING) {
            File pictureFile1 = new File(Environment.getExternalStorageDirectory().toString() + "/yourname", CUTTING_IMAGE_NAME);
            String imgPath = pictureFile1.getAbsolutePath();
            Intent intent = new Intent();
            intent.putExtra("imgPath", imgPath);
            setResult(RESULT_OK,intent);
            finish();
        }
    }
}
  • 再贴一下布局页面



    

    

    
        

    

    

    

    
  • 用法就是在点击打开相机按钮的地方startActivityForResult(),然后在onActivityResult()方法中获取从CameraActivity返回的imgPath,最后可以把值转成file,然后上传服务器就可以了~

你可能感兴趣的:(Android学习之路代码笔记,自定义相机页面,裁剪,自动获取焦点)