通常情况下我们都会用Intent直接跳转到系统相机里面进行拍照,然后在回调里面处理回传的图片;但是如果android底层源码被改写,取消了系统的相机我们要怎么办?
今天来记录一下,在Android中通过调用系统的Camera硬件来完成拍照功能。
首先绘制拍照界面的布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".CameraActivity"> <FrameLayout android:id="@+id/camera_preview" android:layout_width="match_parent" android:layout_height="match_parent"> <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> <Button android:id="@+id/button_capture" android:layout_width="60dp" android:layout_height="wrap_content" android:text="拍照" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" /> </RelativeLayout>
在Activity中封装打开相机硬件的方法
/** * 打开一个Camera */ public static Camera getCameraInstance() { Camera c = null; try { c = Camera.open(); } catch (Exception e) { Log.d(TAG, "打开Camera失败失败"); } return c; }
获取一个最为适配屏幕尺寸的Camera尺寸
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.3; 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; }
初始化Camera硬件的配置
private void initCamera() { Camera.Parameters parameters = mCamera.getParameters();//获取camera的parameter实例 List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();//获取所有支持的camera尺寸 Camera.Size optionSize = getOptimalPreviewSize(sizeList,preview.getWidth(), preview.getHeight());//获取一个最为适配的camera.size parameters.setPreviewSize(optionSize.width,optionSize.height);//把camera.size赋值到parameters mCamera.setParameters(parameters);//把parameters设置给camera mCamera.setDisplayOrientation(0); }
在Activity的onCreate方法中初始化
preview = (FrameLayout) findViewById(R.id.camera_preview); surfaceView = findViewById(R.id.surface_view); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { // 当Surface被创建之后,开始Camera的预览 try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "预览失败"); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // Surface发生改变的时候将被调用,第一次显示到界面的时候也会被调用 if (surfaceHolder.getSurface() == null){ // 如果Surface为空,不继续操作 return; } // 停止Camera的预览 try { mCamera.stopPreview(); } catch (Exception e){ Log.d(TAG, "当Surface改变后,停止预览出错"); } // 在预览前可以指定Camera的各项参数 initCamera(); // 重新开始预览 try { mCamera.setPreviewDisplay(surfaceHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "预览Camera出错"); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }); mCamera = getCameraInstance(); initCamera();
下面是拍照按钮的监听
Button captureButton = (Button) findViewById(R.id.button_capture); captureButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 在捕获图片前进行自动对焦 mCamera.autoFocus(new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { // 从Camera捕获图片 mCamera.takePicture(null, null, mPicture); Toast.makeText(CameraActivity.this,"拍照完成",Toast.LENGTH_SHORT).show(); finish(); } }); } });
下面是监听回调
private PictureCallback mPicture = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // 获取Jpeg图片,并保存在sd卡上 Bitmap bitmap =BitmapFactory.decodeByteArray(data,0,data.length); File file = new File(Environment.getExternalStorageDirectory(),"camera"); if (!file.exists()) { file.mkdirs(); } String fileName = System.currentTimeMillis()+".jpg"; final File pictureFile = new File(file,fileName); final String strFile = pictureFile.getPath(); try { FileOutputStream fos = new FileOutputStream(pictureFile); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); } catch (Exception e) { Log.d(TAG, "保存图片失败"); } } };
运行便可以实现拍照功能了