1 Camera 简介

讲解编解码之前先对Camera进行简单的介绍,本篇介绍完之后只能保证小白会用Camera预览画面,其他的Camera知识会后续讲解。

考虑兼容性依然介绍Camera,目录为android.hardware.Camera,可以看到从api21开始这个类已经被标记为过时,谷歌大大推荐使用android.hardware.Camera2,但是Camera2要从api21才支持,但现在大部分开发还必须以4.+为基础进行开发,所以也只能不听google的坚持使用Camera了。

借助Camera可以利用设备的相机来预览画面,拍照和拍视频。要使用Camera需要在Manifest文件中添加Manifest.permission.CAMERA 权限同时如果要进行自动对焦,还需要特性声明。
完整地权限和特性声明设置为:

 <uses-permission android:name="android.permission.CAMERA" /><uses-feature android:name="android.hardware.camera" /><uses-feature android:name="android.hardware.camera.autofocus" />
//存储权限也需要
<uses-permission android:name="android.permission.WEITE_EXTERNAL_STORAGE" />

注意:如果你只是想简单的实现拍照和拍照视频功能,可以利用Intent打开系统提供的功能。MediaStore.ACTION_IMAGE_CAPTURE 拍摄照片;MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频;

利用Camera想要拍照,需要如下使用步骤:

  • 1 利用open(int)获取Camera实例
  • 2 利用getParameters()获取默认设置,如果需要利用setParameters(Camera.Parameters)进行参数设置
  • 3 利用setDisplayOrientation(int)函数设置正确的预览方向
  • 4 想要预览,需要配合SurfaceView,利用setPreviewDisplay(SurfaceHolder)设置SurfaceView的SurfaceHolder用于预览。
  • 5 调用startPreview()开始预览,拍照之前必须已经开始预览
  • 6 takePicture 拍摄照片
  • 7 调用takePickture后预览会停止,想要继续预览需要调用startPreview()函数
  • 8 调用stopPreview()停止预览
  • 9 调用release()释放资源,为了节省资源在Activity.onPause是调用停止预览,在onResume是开始预览。

2 打开相机

检查是否有相机
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}

获取相机数目:
Camera.getNumberOfCameras()

open或open(int)打开相机
open开启默认相机(后置相机),open(int)开启特定相机,打开相机是可能失败的,所以一定要检查相机是否打开成功,判断Camera是否为null, mCamera = Camera.open(cameraID);
后置相机和前置相机id常量:CameraInfo.CAMERA_FACING_BACK, CameraInfo.CAMERA_FACING_FRONT
打开特定相机Camera.open(cameraid)。

Camera.getCameraInfo() 可以获取CameraInfo,可以知道相机是位于前面还是后面。

public static class CameraInfo {public static final int CAMERA_FACING_BACK = 0;public static final int CAMERA_FACING_FRONT = 1;/*** 这个值就是标明相机是前置还是后置* CAMERA_FACING_BACK or CAMERA_FACING_FRONT.*/public int facing;public int orientation;};

3 相机预览方向设置

相机的方向(0,90,180,270)有四种,预览需要设置正确的方向和尺寸,预览的图片才不会变形,可以利用Camera.setDisplayOrientaion(int)设置相机的预览方向。可设置的参数有0,90,180,270,默认为0,是指手机的左侧为摄像头顶部画面,所以相机默认为横屏,如果要竖屏预览,就需要设置90度。
如果想让相机跟随设备方向变化,改变预览的方向,需要结合相机已经旋转的角度和屏幕旋转的角度以及相机的前后(前置相机和后置相机预览界面是不同的,前置有镜面效果),最好固定Activity的方向。
特别注意:
设置预览角度,setDisplayOrientation本身只能改变预览的角度previewFrameCallback以及拍摄出来的照片是不会发生改变的,拍摄出来的照片角度依旧不正常的,所以拍摄最后得到的照片需要自行处理(旋转)。
在布局发生改变时要重新设置相机预览方向。

一般设置相机方向的通用方法:

public static int calculateCameraPreviewOrientation(Activity activity) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(mCameraID, info);int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (rotation) {case Surface.ROTATION_0:degrees = 0;break;case Surface.ROTATION_90:degrees = 90;break;case Surface.ROTATION_180:degrees = 180;break;case Surface.ROTATION_270:degrees = 270;break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360;} else {result = (info.orientation - degrees + 360) % 360;}return result;
}

最终算出来的值result是0,90,180,270中的一个,要获取info.orientation的目的是不知道相机默认角度是多少。

4 设置预览大小

相机的宽度和高度跟屏幕坐标不一样,相机的宽高和手机屏幕的宽高是反过来的。例如如果设置展示预览图的SurfaceView的宽高比3:4,选取Camera预览图的尺寸时应该是4:3(因为预览默认对应横向,如果要竖向显示时,就是反过来的。)。

设备上的相机支持的预览大小是确定的,当然也不是只有一种,会有很多种,我们只能从它支持的大小的列表中选取一个最接近我们需要比例的宽高。

为了保证相机预览画面不变形,预览界面大小的设置必须和选取的相机支持的尺寸的宽高的比例相同。
举个例子,获取手机上支持的所有预览尺寸:

Camera.Parameters parameters = camera.getParameters();
parameters.getSupportedPreviewSizes()

结果:
==支持的预览尺寸=宽高= 176 144
==支持的预览尺寸=宽高= 320 240
==支持的预览尺寸=宽高= 352 288
==支持的预览尺寸=宽高= 640 480
==支持的预览尺寸=宽高= 1280 720
==支持的预览尺寸=宽高= 1280 960
==支持的预览尺寸=宽高= 176 144
==支持的预览尺寸=宽高= 320 240
==支持的预览尺寸=宽高= 352 288
==支持的预览尺寸=宽高= 640 480
==支持的预览尺寸=宽高= 1280 720
==支持的预览尺寸=宽高= 1280 960

再次强调:可以看到getSupportedPreviewSizes获取了相机支持的所有预览尺寸,注意一点,屏幕的宽高是按照竖屏获取的,而getSupportedPreviewSizes获得支持的尺寸是按照横屏来说的,也就是说我们上面获取的宽高实际上是反过来的,高是宽,宽是高,不知道大家理解没。

例如选取上面获取到的640:480,其实对应到竖向屏幕上是480:640。,如果我们选取640x480的尺寸,那么预览Camera的SurfaceView的宽高比也必须为3:4,这样预览的画面才不会变形,否则可能导致变形。
expectWidth和expectHeight 分别对应期望的宽和高,是对应相机的宽高,所以如果希望在view中看到的是640*480的预览,则期望宽高应该写入3:4的宽高,也就是把希望的对应view的宽高反过来。

查找最合适尺寸的规则:
找出最合适的尺寸,规则如下:
1.将尺寸按比例分组,找出比例最接近屏幕比例的尺寸组
2.在比例最接近的尺寸组中找出最接近屏幕尺寸且大于屏幕尺寸的尺寸
3.如果没有找到,则忽略2中第二个条件再找一遍,应该是最合适的尺寸了

/*** 找出最合适的尺寸,规则如下:* 1.将尺寸按比例分组,找出比例最接近屏幕比例的尺寸组* 2.在比例最接近的尺寸组中找出最接近屏幕尺寸且大于屏幕尺寸的尺寸* 3.如果没有找到,则忽略2中第二个条件再找一遍,应该是最合适的尺寸了*/
private static Camera.Size findProperSize(Point surfaceSize, List<Camera.Size> sizeList) {if (surfaceSize.x <= 0 || surfaceSize.y <= 0 || sizeList == null) {return null;}int surfaceWidth = surfaceSize.x;int surfaceHeight = surfaceSize.y;List<List<Camera.Size>> ratioListList = new ArrayList<>();for (Camera.Size size : sizeList) {addRatioList(ratioListList, size);}final float surfaceRatio = (float) surfaceWidth / surfaceHeight;List<Camera.Size> bestRatioList = null;float ratioDiff = Float.MAX_VALUE;for (List<Camera.Size> ratioList : ratioListList) {float ratio = (float) ratioList.get(0).width / ratioList.get(0).height;float newRatioDiff = Math.abs(ratio - surfaceRatio);if (newRatioDiff < ratioDiff) {bestRatioList = ratioList;ratioDiff = newRatioDiff;}}Camera.Size bestSize = null;int diff = Integer.MAX_VALUE;assert bestRatioList != null;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (size.height >= surfaceHeight && newDiff < diff) {bestSize = size;diff = newDiff;}}if (bestSize != null) {return bestSize;}diff = Integer.MAX_VALUE;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (newDiff < diff) {bestSize = size;diff = newDiff;}}return bestSize;
}private static void addRatioList(List<List<Camera.Size>> ratioListList, Camera.Size size) {float ratio = (float) size.width / size.height;for (List<Camera.Size> ratioList : ratioListList) {float mine = (float) ratioList.get(0).width / ratioList.get(0).height;if (ratio == mine) {ratioList.add(size);return;}}List<Camera.Size> ratioList = new ArrayList<>();ratioList.add(size);ratioListList.add(ratioList);
}

设置SurfaceView的宽高为3:4的图像。

设置SurfaceView的宽高为4:3的图像,明显变形了。

5 设置拍摄图片大小

调用camera的takePicture方法后,获得拍照的图像数据,如何设置保存图片的大小,类似设置预览大小,利用parameters.getSupportedPictureSizes()可以获取支持的保存图片的大小,图片尺寸同样只能从支持的列表中选取一个设置。

picturesize和previewsize的宽高比也要保证一致,否则获取的图片会将preview时的图像裁剪成picturesize的比例。
previewsize的分辨率,只会影响预览时的分辨率,不会影响获取图片的分辨率,所以preview只是确定了图像的取景最大范围(所谓的取景范围就是展示多大的画面),最终图片的分辨率是由picturesize来决定。

6 Camera设置帧率

 /*** 选择合适的FPS* @param parameters* @param expectedThoudandFps 期望的FPS* @return*/public static int chooseFixedPreviewFps(Camera.Parameters parameters, int expectedThoudandFps) {List<int[]> supportedFps = parameters.getSupportedPreviewFpsRange();for (int[] entry : supportedFps) {if (entry[0] == entry[1] && entry[0] == expectedThoudandFps) {parameters.setPreviewFpsRange(entry[0], entry[1]);return entry[0];}}int[] temp = new int[2];int guess;parameters.getPreviewFpsRange(temp);if (temp[0] == temp[1]) {guess = temp[0];} else {guess = temp[1] / 2;}return guess;}

getSupportedPreviewFpsRange函数可以获取设备支持的帧率,fps不是越高越好,FPS不宜过高,一般30fps足够了。

7 如何读取Camera的NV21数据和YUV数据

给camera对象设置一个 Camera.PreviewCallback,在这个回调中实现一个方法onPreviewFrame(byte[] data, Camera camera),就可以去Camera预览图片时的数据。

当然如果设置了camera.setPreviewCallback(callback),onPreviewFrame这个方法会被一直调用,可以在摄像头对焦成功后设置camera.setOneShotPreviewCallback(previewCallback),这样设置onPreviewFrame这个方法就会被调用一次,处理data数据,bitmap来做相应的处理就行了。这两个方法都是系统自动配置缓冲区。
setPreviewFormat 函数可以设置预览是onPreviewFrame返回数据的格式。
最常见的获取的数据格式为NV21,如果不设置默认返回数据也是NV21编码的数据。

Camera.Parameters parameters = mCamera.getParameters();
parameters.setRecordingHint(true);
{//设置获取数据的格式parameters.setPreviewFormat(ImageFormat.NV21);//parameters.setPreviewFormat(ImageFormat.YV12);//通过setPreviewCallback方法监听预览的回调:byte[] imageByte;Bitmap bitmap;mCamera.setPreviewCallback(new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] bytes, Camera camera) {//这里面的Bytes的数据就是NV21格式的数据,或者YV12的数据Camera.Size previewSize = camera.getParameters().getPreviewSize();//获取尺寸,格式转换的时候要用到BitmapFactory.Options newOpts = new BitmapFactory.Options();newOpts.inJustDecodeBounds = true;YuvImage yuvimage = new YuvImage(data,ImageFormat.NV21,previewSize.width,previewSize.height,null);baos = new ByteArrayOutputStream();yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 100, baos);// 80--JPG图片的质量[0-100],100最高imageByte = baos.toByteArray();//将imageByte转换成bitmapBitmapFactory.Options options = new BitmapFactory.Options();options.inPreferredConfig = Bitmap.Config.RGB_565;bitmap = BitmapFactory.decodeByteArray(imageByte, 0, imageByte.length, options);}});}
mCamera.setParameters(parameters);

注意,onPreviewFrame()方法跟Camera.open()是运行于同一个线程,所以为了防止onPreviewFrame()会阻塞UI线程,将Camera.open()放置在子线程中运行。

为什么要用到这个函数,因为如果调用takePicture别管怎么设置界面都会卡顿,如果通过onPreviewFrame的回调处理函数,不会导致界面卡顿。

setPreviewCallbackWithBuffer 类似setPreiewCallback,一般配合setPreviewCallback使用
setPreviewCallbackWithBuffer (Camera.PreviewCallback cb) 要求指定一个字节数组作为缓冲区,用于预览帧数据,这样能够更好的管理预览帧数据时使用的内存。

setPreviewCallbackWithBuffer需要在startPreview()之前调用,因为setPreviewCallbackWithBuffer使用时需要指定一个字节数组作为缓冲区,用于预览帧数据,所以我们需要在setPreviewCallbackWithBuffer之前调用addCallbackBuffer,这样onPreviewFrame的data才有值。然后需要在onPreviewFrame中调用,如果在onPreviewFrame中不调用,那么预览帧数据就不会回调给onPreviewFrame。

代码示例:

//通过setPreviewCallback方法监听预览的回调:mCamera.setPreviewCallback(new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] bytes, Camera camera) {//这里面的Bytes的数据就是NV21格式的数据,或者YUV_420_888的数据}});mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {}});}mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {}});

8 其他

camera的切换(前后摄像头)

要先释放前一个Camera,然后打开新相机,打开预览。

takePicture获取图片

调用takePicture后预览会停止,需用重新调用startPreview才能再次开始预览。预览开始后,就可以通过Camera.takePicture()方法拍摄一张照片,返回的照片数据通过Callback接口获取。
takePicture()接口可以获取三个类型的照片:
第一个,ShutterCallback接口,在拍摄瞬间瞬间被回调,通常用于播放“咔嚓”这样的音效;
第二个,PictureCallback接口,返回未经压缩的RAW类型照片;
第三个,PictureCallback接口,返回经过压缩的JPEG类型照片;

是否支持自动对焦

List modes = Parameters.getSupportedFocusModes();
modes.contains(Camera.Parameters.FOCUS_MODE_AUTO);
getSupportedFocusModes函数获取所有的焦点模式,FOCUS_MODE_AUTO标识自动对焦,对焦方式还有FOCUS_MODE_CONTINUOUS_VIDEO使用视频录制,FOCUS_MODE_CONTINUOUS_PICTURE 用于拍照。

9 简单实例:

public class Main23Activity extends AppCompatActivity implements SurfaceHolder.Callback ,SensorEventListener{private static int mOrientation = 0;private static int mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;private CustomSurfaceView mSurfaceView;private SurfaceHolder mSurfaceHolder;private Camera mCamera;private boolean havePermission = false;private Button btnFocus;private Button btnTakePic;private Button btnRestar;private Button btnChange;private int useWidth;private int useHeight;private SensorManager mSensorManager;private Sensor mSensor;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main23);btnFocus = findViewById(R.id.focus);btnTakePic = findViewById(R.id.takepic);btnRestar = findViewById(R.id.restar);btnChange = findViewById(R.id.change);btnChange.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {switchCamera();}});btnRestar.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(mCamera != null){mCamera.startPreview();}}});btnFocus.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mCamera != null && mCameraID == Camera.CameraInfo.CAMERA_FACING_BACK){mCamera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {if(success){Toast.makeText(Main23Activity.this,"对焦成功",Toast.LENGTH_SHORT).show();}else{}}});}}});btnTakePic.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(mCamera!= null){mCamera.takePicture(null, null, new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {// 获取Jpeg图片,并保存在sd卡上String path = Environment.getExternalStorageDirectory().getPath()  +"/focus/";File pathDir = new File(path);if (!pathDir.exists()){pathDir.mkdir();}File pictureFile = new File(path+ "focusdemo.jpg");if (pictureFile.exists()){pictureFile.delete();}try {FileOutputStream fos = new FileOutputStream(pictureFile);fos.write(data);fos.close();} catch (Exception e) {}}});}}});mSensorManager = (SensorManager) Main23Activity.this.getSystemService(Activity.SENSOR_SERVICE);mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// 加速度// Android 6.0相机动态权限检查,省略了if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)== PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED) {havePermission = true;init();} else {havePermission = false;ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);}}@Overrideprotected void onStop() {super.onStop();mSensorManager.unregisterListener(this,mSensor);}public void releaseCamera(){if (mCamera != null) {mSurfaceHolder.removeCallback(this);mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.lock();mCamera.release();mCamera = null;}}public void switchCamera(){if(mCameraID ==  Camera.CameraInfo.CAMERA_FACING_BACK){mCameraID =  Camera.CameraInfo.CAMERA_FACING_FRONT;}else{mCameraID =  Camera.CameraInfo.CAMERA_FACING_BACK;}try {initCamera();} catch (Exception e) {e.printStackTrace();}}public void init(){if(mSurfaceView == null){mSurfaceView = findViewById(R.id.surfaceview);mSurfaceView.setCustomEvent(new CustomSurfaceView.ONTouchEvent() {@Overridepublic void onTouchEvent(MotionEvent event) {handleFocus(event, mCamera);}});mSurfaceHolder = mSurfaceView.getHolder();mSurfaceHolder.addCallback(this);WindowManager wm = (WindowManager) Main23Activity.this.getSystemService(Context.WINDOW_SERVICE);int width = wm.getDefaultDisplay().getWidth();LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mSurfaceView.getLayoutParams();layoutParams.width = width;layoutParams.height = width*4/3;useWidth = width;useHeight = width*4/3;mSurfaceView.setLayoutParams(layoutParams);}}private void initCamera() {if (mCamera != null){releaseCamera();System.out.println("===================releaseCamera=============");}mCamera = Camera.open(mCameraID);System.out.println("===================openCamera=============");if (mCamera != null){try {mCamera.setPreviewDisplay(mSurfaceHolder);} catch (IOException e) {e.printStackTrace();}Camera.Parameters parameters = mCamera.getParameters();parameters.setRecordingHint(true);{//设置获取数据parameters.setPreviewFormat(ImageFormat.NV21);//parameters.setPreviewFormat(ImageFormat.YUV_420_888);//通过setPreviewCallback方法监听预览的回调:mCamera.setPreviewCallback(new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] bytes, Camera camera) {//这里面的Bytes的数据就是NV21格式的数据,或者YUV_420_888的数据}});}if(mCameraID == Camera.CameraInfo.CAMERA_FACING_BACK){parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);}mCamera.setParameters(parameters);calculateCameraPreviewOrientation(this);Camera.Size tempSize = setPreviewSize(mCamera, useHeight,useWidth);{//此处可以处理,获取到tempSize,如果tempSize和设置的SurfaceView的宽高冲突,重新设置SurfaceView的宽高}setPictureSize(mCamera,  useHeight,useWidth);mCamera.setDisplayOrientation(mOrientation);int degree = calculateCameraPreviewOrientation(Main23Activity.this);mCamera.setDisplayOrientation(degree);mCamera.startPreview();}}@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//当SurfaceView变化时也需要做相应操作,这里未做相应操作if (havePermission){initCamera();}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {mCamera.stopPreview();}private void setPictureSize(Camera camera ,int expectWidth,int expectHeight){Camera.Parameters parameters = camera.getParameters();Point point = new Point(expectWidth, expectHeight);Camera.Size size = findProperSize(point,parameters.getSupportedPreviewSizes());parameters.setPictureSize(size.width, size.height);camera.setParameters(parameters);}private Camera.Size setPreviewSize(Camera camera, int expectWidth, int expectHeight) {Camera.Parameters parameters = camera.getParameters();Point point = new Point(expectWidth, expectHeight);Camera.Size size = findProperSize(point,parameters.getSupportedPictureSizes());parameters.setPictureSize(size.width, size.height);camera.setParameters(parameters);return size;}/*** 找出最合适的尺寸,规则如下:* 1.将尺寸按比例分组,找出比例最接近屏幕比例的尺寸组* 2.在比例最接近的尺寸组中找出最接近屏幕尺寸且大于屏幕尺寸的尺寸* 3.如果没有找到,则忽略2中第二个条件再找一遍,应该是最合适的尺寸了*/private static Camera.Size findProperSize(Point surfaceSize, List<Camera.Size> sizeList) {if (surfaceSize.x <= 0 || surfaceSize.y <= 0 || sizeList == null) {return null;}int surfaceWidth = surfaceSize.x;int surfaceHeight = surfaceSize.y;List<List<Camera.Size>> ratioListList = new ArrayList<>();for (Camera.Size size : sizeList) {addRatioList(ratioListList, size);}final float surfaceRatio = (float) surfaceWidth / surfaceHeight;List<Camera.Size> bestRatioList = null;float ratioDiff = Float.MAX_VALUE;for (List<Camera.Size> ratioList : ratioListList) {float ratio = (float) ratioList.get(0).width / ratioList.get(0).height;float newRatioDiff = Math.abs(ratio - surfaceRatio);if (newRatioDiff < ratioDiff) {bestRatioList = ratioList;ratioDiff = newRatioDiff;}}Camera.Size bestSize = null;int diff = Integer.MAX_VALUE;assert bestRatioList != null;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (size.height >= surfaceHeight && newDiff < diff) {bestSize = size;diff = newDiff;}}if (bestSize != null) {return bestSize;}diff = Integer.MAX_VALUE;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (newDiff < diff) {bestSize = size;diff = newDiff;}}return bestSize;}private static void addRatioList(List<List<Camera.Size>> ratioListList, Camera.Size size) {float ratio = (float) size.width / size.height;for (List<Camera.Size> ratioList : ratioListList) {float mine = (float) ratioList.get(0).width / ratioList.get(0).height;if (ratio == mine) {ratioList.add(size);return;}}List<Camera.Size> ratioList = new ArrayList<>();ratioList.add(size);ratioListList.add(ratioList);}/*** 排序* @param list*/private static void sortList(List<Camera.Size> list) {Collections.sort(list, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size pre, Camera.Size after) {if (pre.width > after.width) {return 1;} else if (pre.width < after.width) {return -1;}return 0;}});}/*** 设置预览角度,setDisplayOrientation本身只能改变预览的角度* previewFrameCallback以及拍摄出来的照片是不会发生改变的,拍摄出来的照片角度依旧不正常的* 拍摄的照片需要自行处理* 这里Nexus5X的相机简直没法吐槽,后置摄像头倒置了,切换摄像头之后就出现问题了。* @param activity*/public static int calculateCameraPreviewOrientation(Activity activity) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(mCameraID, info);int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (rotation) {case Surface.ROTATION_0:degrees = 0;break;case Surface.ROTATION_90:degrees = 90;break;case Surface.ROTATION_180:degrees = 180;break;case Surface.ROTATION_270:degrees = 270;break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360;} else {result = (info.orientation - degrees + 360) % 360;}mOrientation = result;System.out.println("=========orienttaion============="+result);return result;}@Overrideprotected void onResume() {super.onResume();mSensorManager.registerListener(this, mSensor,SensorManager.SENSOR_DELAY_NORMAL);if (havePermission && mCamera != null)mCamera.startPreview();}@Overrideprotected void onPause() {super.onPause();if (havePermission && mCamera != null)mCamera.stopPreview();}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode) {// 相机权限case 100:havePermission = true;init();break;}}/*** 转换对焦区域* 范围(-1000, -1000, 1000, 1000)*/private  Rect calculateTapArea(float x, float y,  int width, int height, float coefficient) {float focusAreaSize = 200;int areaSize = (int) (focusAreaSize * coefficient);int surfaceWidth = width;int surfaceHeight = height;int centerX = (int) (x / surfaceHeight * 2000 - 1000);int centerY = (int) (y / surfaceWidth * 2000 - 1000);int left = clamp(centerX - (areaSize / 2), -1000, 1000);int top = clamp(centerY - (areaSize / 2), -1000, 1000);int right = clamp(left + areaSize, -1000, 1000);int bottom = clamp(top + areaSize, -1000, 1000);return new Rect(left, top, right, bottom);}//不大于最大值,不小于最小值private  int clamp(int x, int min, int max) {if (x > max) {return max;}if (x < min) {return min;}return x;}private  void handleFocus(MotionEvent event, Camera camera) {int viewWidth = useWidth;int viewHeight = useHeight;Rect focusRect = calculateTapArea(event.getX(), event.getY(),  viewWidth, viewHeight,1.0f);//一定要首先取消camera.cancelAutoFocus();Camera.Parameters params = camera.getParameters();if (params.getMaxNumFocusAreas() > 0) {List<Camera.Area> focusAreas = new ArrayList<>();focusAreas.add(new Camera.Area(focusRect, 800));params.setFocusAreas(focusAreas);} else {//focus areas not supported}//首先保存原来的对焦模式,然后设置为macro,对焦回调后设置为保存的对焦模式final String currentFocusMode = params.getFocusMode();params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);camera.setParameters(params);camera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {//回调后 还原模式Camera.Parameters params = camera.getParameters();params.setFocusMode(currentFocusMode);camera.setParameters(params);if(success){Toast.makeText(Main23Activity.this,"对焦区域对焦成功",Toast.LENGTH_SHORT).show();}}});}@Overridepublic void onSensorChanged(SensorEvent event) {//手机移动一段时间后静止,然后静止一段时间后进行对焦// 读取加速度传感器数值,values数组0,1,2分别对应x,y,z轴的加速度if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {int x = (int) event.values[0];int y = (int) event.values[1];int z = (int) event.values[2];}}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}
}


上面的实例代码支持,触摸对焦,自动对焦,拍照,摄像头切换,其他更多高级内容后续会接着讲。

抱歉源码已经丢失,CustomSurfaceView只是内部添加了一个点击回调函数(点击之后展示一个方框View)为实现点击聚焦,可以直接使用原生surfaceview,添加一个点击事件(替换mSurfaceView.setCustomEvent)内部调用handleFocus。

Android Camera基本用法一相关推荐

  1. android camera 降低帧率_Android性能问题分析之bugreport

    Android手机性能问题一直是用户关注的重点,分析性能问题则成为工程师日常工作的一部分.根据问题的类型通常有适合的工具可供使用,比如systrace ,traceview,simpleperf等可视 ...

  2. Android Camera 通过V4L2与kernel driver的完整交互过程

    Android Camera 通过V4L2与kernel driver的完整交互过程 之前在  Android Camera 的执行流程   http://blog.chinaunix.net/uid ...

  3. Android Camera设置setPreviewCallback实现onPreviewFrame接口实时截取每一帧视频流数据

    1 概述 通过Android Camera拍摄预览中设置setPreviewCallback实现onPreviewFrame接口,实时截取每一帧视频流数据 2 知识点 ① Android Camera ...

  4. qcom Android Camera【转】

    本文转载自:http://blog.csdn.net/Wilsonboliu/article/details/54949196 1.总体架构 Android Camera 框架从整体上看是一个 cli ...

  5. android camera之nv21旋转

    android camera之nv21旋转 这周做的一个android的camera开发,需要获取到视频帧数据,并且需要是nv21格式的byte数组,并且视频帧的图像需要是正方向的.和android相 ...

  6. 【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )

    文章目录 安卓直播推流专栏博客总结 一. Camera 传感器方向简介 二. Camera 图像传感器横向显示数据 三. Camera 图像传感器纵向显示数据 四. 设置 Camera 预览数据方向 ...

  7. 【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )

    文章目录 安卓直播推流专栏博客总结 一. Android 端数据采集涉及到的相关概念 二. Camera 预览图像尺寸设置 三. 获取摄像头采集的数据格式 安卓直播推流专栏博客总结 Android R ...

  8. android camera(三):camera V4L2 FIMC

    关键词: android  camera CMM 模组 camera参数  CAMIF   V4L2   平台信息: 内核: linux 系统: android 平台:S5PV310(samsung ...

  9. android camera(二):摄像头工作原理、s5PV310 摄像头接口(CAMIF)

    关键词: android  camera CMM 模组 camera参数  CAMIF 平台信息: 内核: linux 系统: android 平台:S5PV310(samsung exynos 42 ...

最新文章

  1. python开源项目博客_Blog_mini首页、文档和下载 - Python Flask开源博客 - OSCHINA - 中文开源技术交流社区...
  2. BZOJ 4898 Luogu P3778 [APIO2017]商旅 (分数规划、最短路)
  3. how drop down list description is displayed by UI framework
  4. 矩阵特征值的物理意义
  5. 1 2014年12月电大远程网络教育计算机统考 最 新 题 库,2014年12月份电大远程网络教育计算机应用基础统考题库试卷6...
  6. 为Go编译的Windows程序加入资源文件
  7. 获取屏幕尺寸、状态栏、标题栏高度
  8. java dom4j 写xml文件_Java实现——Dom4j读写XML文件
  9. verilog 写rtl注意事项_RTL基本知识:Verilog常见错误
  10. 华为鸿蒙os2.0beta版发布会,华为发布鸿蒙OS Beta版,华为鸿蒙2.0适应范围以及优势所在...
  11. BP神经网络及其学习算法
  12. 74ls20设计半加器_实验二++组合逻辑电路的设计与测试.ppt
  13. dataworks手册_DataWorks 使用教程
  14. python批量查询(excel)数据
  15. 什么是CPU密集型?什么是IO密集型?
  16. 给 Android 开发者的 Gradle 入门指南
  17. SQL数据库管理—DBCC数据库修复
  18. ESP32 开发笔记(四)littleVGL LVGL 控件学习 Arc 弧形控件
  19. PDF不能打印怎么办?
  20. 三星Samsung手机平板安装Google Play商店,安装谷歌服务框架,解决闪退奔溃GMS

热门文章

  1. C语言通过Windows命令行编译和运行程序
  2. 高斯输出文件批量提取单点能:Shell脚本处理
  3. c语言字符相关函数,c语言字符相关函数
  4. CronTrigger定时任务
  5. 汇编语言相关指令介绍(二)
  6. ubuntu的分辨率突然变得很奇怪或者ubuntu 上qq无法打开
  7. 第1个Qt项目:计算器
  8. Linux —— OpenCv编译安装
  9. 页面跳转javascript操作referer
  10. 企业经营数据分析非得BI不可吗?