前言 写的第二版了,第一版直接将相机实现部分和cocos放一块了,显的很乱,代码也很乱,所以重构了一遍。
这一遍将Android相机部分单独抽离部分。是一个AndroidStudio工程,并将项目环境还原成cocos项目中minSdk为19,targetSdk为21。相机预览部分layout同样采用动态添加的方式。达到后面直接将代码放到cocos中就能直接使用的目的。
项目选用SurfaceView + Camera(相对Camera2)实现。原因是可以兼容老工程使用,基础的一些功能是完全够用的。内存占用方面据说也是比TextureView更低。
同样这一版也同样存在着一些问题:
上面说到minSdk为19,targetSdk为21。也就是在Android6.0之前的。打包出来设备默认开启清单(Manifest)中所有权限,无需动态申请权限。放到23版本的工程中,先确认是否需要进行动态申请权限,并解决。刚好笔者之前也对这部分做过总结,可以参考下:android中的权限介绍 ,android动态申请权限 (TIP: 为了解决安卓高版本中权限被手动禁止导致无法正常使用的情况,就弹出提示让玩家跳转到设置界面进行手动配置。)
暂不支持设备设置自动旋转屏幕,旋转屏幕会导致关闭相机。不影响使用,后续再研究,有知道的希望能后不吝赐教,在下方留言。
加一波权限 1 2 3 4 5 6 7 8 9 <uses-permission android:name ="android.permission.CAMERA" /> <uses-permission android:name ="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name ="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name ="android.hardware.camera" /> <uses-feature android:name ="android.hardware.camera.autofocus" />
正式开始 把相机的各种操作单独抽离出来作为一个工具类CameraHelper.java
,包括:判断是否有相机,开启相机,销毁相机,切换相机,拍照等接口。
判断是否有相机 使用了Camera.getNumberOfCameras()
接口获取相机数量来判断是否有相机,发现无相机的设备也会返回2。最终选择尝试打开相机捕获异常来判断有没有相机。
1 2 3 4 5 6 7 8 9 10 public boolean HasCamera () { try { mCamera = Camera.open(cameraID); mCamera.release(); mCamera=null ; }catch (Exception exception){ return false ; } return true ; }
打开相机 1 2 3 4 5 6 7 8 9 10 11 12 13 public void OpenCamera (SurfaceHolder holder) { try { Log.d(TAG,"打开相机: " + cameraID); mCamera = Camera.open(cameraID); Camera.getCameraInfo(cameraID, cameraInfo); initCamera(); setDispalyRotation(); StartPreview(holder); } catch (Exception ex){ ex.printStackTrace(); } }
配置相机 打开相机后,首先拿到相机的配置参数,然后再初始化相机的自动或手动对焦,图片格式,预览尺寸等参数,最后再应用到相机。相机预览尺寸有点坑,后面也会开篇介绍,点我->调教android相机全屏预览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private void initCamera () { try { parameters = mCamera.getParameters(); if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); } if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)){ parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); } parameters.setPictureFormat(ImageFormat.JPEG); parameters.setPreviewFormat(ImageFormat.NV21); parameters.setExposureCompensation(0 ); DisplayMetrics metrics = new DisplayMetrics (); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); int screenWidth = metrics.widthPixels > metrics.heightPixels ? metrics.widthPixels : metrics.heightPixels; int screenHeight = metrics.widthPixels < metrics.heightPixels ? metrics.widthPixels : metrics.heightPixels; Point previousSize = findBestPreviewSizeValue(parameters.getSupportedPreviewSizes(), screenWidth, screenHeight); parameters.setPreviewSize(previousSize.x, previousSize.y); mCamera.setParameters(parameters); }catch (Exception ex){ ex.printStackTrace(); } }
开始预览 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void StartPreview (SurfaceHolder holder) { if (mCamera != null ){ try { Log.d(TAG, "开始预览" ); mCamera.setPreviewDisplay(holder); }catch (IOException ex){ ex.printStackTrace(); } if (!isPrevious){ mCamera.startPreview(); isPrevious = true ; } } }
结束预览 1 2 3 4 5 6 7 8 9 public void StopPreview () { if (mCamera != null ){ Log.d(TAG, "StopPreview" ); if (isPrevious) { mCamera.stopPreview(); isPrevious = false ; } } }
销毁相机 1 2 3 4 5 6 7 8 9 public void DestoryCamera () { if (mCamera != null ){ Log.d(TAG,"销毁相机" ); mCamera.setPreviewCallback(null ); mCamera.stopPreview(); mCamera.release(); mCamera=null ; } }
切换相机 android后置相机索引是0,前置相机索引是1。
1 2 3 4 5 public void SwitchCamera (SurfaceHolder holder) { cameraID = cameraID == 0 ? 1 : 0 ; DestoryCamera(); OpenCamera(holder); }
拍照 camera正统的拍照方式是使用takePicture
这个API。但是使用这种方式会导致拍照时间会几秒,体验不好。后面有一篇专门解决拍照缓慢的问题:android相机拍照缓慢解决方案
自定义SurfaceView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class CameraSurfaceView extends SurfaceView implements SurfaceHolder .Callback { private CameraHelper cameraHelper; private SurfaceHolder surfaceHolder; public CameraSurfaceView (Activity activity, CameraHelper cameraHelper) { super (activity); this .cameraHelper = cameraHelper; this .surfaceHolder = this .getHolder(); surfaceHolder.addCallback(this ); } @Override public void surfaceCreated (SurfaceHolder holder) { if (cameraHelper != null ){ cameraHelper.OpenCamera(holder); } } @Override public void surfaceChanged (SurfaceHolder holder, int format, int width, int height) { if (cameraHelper!=null ){ cameraHelper.StopPreview(); cameraHelper.StartPreview(holder); } } @Override public void surfaceDestroyed (SurfaceHolder holder) { if (cameraHelper!=null ) cameraHelper.DestoryCamera(); } }
安卓中的例子 随便搞了3个按钮,分别是打开相机,拍照和关闭相机,下面分别实现这些功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 if (cameraHelper.HasCamera()){ cameraSurfaceView = new CameraSurfaceView (Cocos2dxActivity.this , cameraHelper); layout.addView(cameraSurfaceView, 0 ); cameraHelper.OpenCamera(cameraSurfaceView.getHolder()); }else { } cameraHelper.TakePicture(new Camera .PreviewCallback() { @Override public void onPreviewFrame (byte [] data, Camera camera) { Bitmap bmp = getBmpPicData(data, camera); saveBitmap(bmp); } }); cameraHelper.DestoryCamera(); layout.removeView(cameraSurfaceView); OpenCamera.setEnabled(true );
解决设备无相机或没权限问题 那就弹出一个对话框,让用户跳转到设置界面检查是不是权限没开,如下:
1 2 3 Intent intent = new Intent (Settings.ACTION_APPLICATION_DETAILS_SETTINGS);intent.setData(Uri.fromParts("package" ,this .getPackageName(), null )); startActivity(intent);
源文件 CameraHelper.java
CameraSurfaceView.java
Cocos2dxActivity.java
AndroidManifest.xml
工程地址 https://gitlab.uuzu.com/chentc/CustomAndroidCamera