该文章 主要使用 自定义 surfaceview 及 camera 知识点,来实现一个自定义的拍照 、切换闪光灯 和 前后摄像头的功能。阅读需要消耗时间 :15分钟+ 。内容比较简单算是 开发相机的过程记录把。

<文章已收录于 Wan android 网站中>

GitHub Demo 地址 :yangmingchuan / SunCamera

1. 调用原生相机

在记录自定义camera相机前,先简单提一下调用系统相机和获取图片返回值的方式。

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(mCurrentPhotoFile));
startActivityForResult(intent, CAMERA_WITH_DATA);

其中 mCurrentPhotoFile 为图片返回的名称。

2. 自定义相机

对于程序在本地真机的运行效果这里先放一个效果图,如果是读者需要的效果,则您可以接着往下看。如果急需的则可以在文章末尾直接查看完整的 代码。


看完 效果图后,我们简要的说一下 自定义 camera 的过程

1.创建显示相机画面的布局,Android已经为我们选定好SurfaceView
2.创建预览界面,创建继承自SurfaceView并实现SurfaceHolder接口的拍摄预览类。有了拍摄预览类,即可创建一个布局文件,将预览画面与设计好的用户界面控件融合在一起,实时显示相机的预览图像。
3.设置拍照监听器,给用户界面控件绑定监听器,使其能响应用户操作, 开始拍照过程。
4.拍照并保存文件,将拍摄获得的图像输出保存成各种常用格式的图片。
5.当相机使用完毕后,必须正确地将其释放,以免其它程序访问使用时发生冲突。

针对 camera 和 camera2 自定义的不同

1.camera 中使用的显示的对象为 SurfaceView ,SurfaceView是一个有自己Surface的View。界面渲染可以放在单独线程而不是主线程中。它更像是一个Window,自身不能做变形和动画。

2.camera2 中使用的显示的载体为 TextureView ,同样也有自己的Surface。但是它只能在拥有硬件加速层层的Window中绘制,它更像是一个普通View,可以做变形和动画。

2.1 添加需要的权限

目前测试手机:小米6 ,android 版本:27 。
对于部分危险权限除了清单文件中的声明,还需要动态申请

// 拍照权限
<uses-permission android:name="android.permission.CAMERA" />
// 网络
<uses-permission android:name="android.permission.INTERNET" />
// 读写本地存储
<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.autofocus" />

接下来会是该Demo中主要用于 控件声明和动态权限所需要添加的第三方库

// butterknife
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'// permission
implementation 'com.yanzhenjie:permission:2.0.0-rc4'
implementation 'com.android.support:exifinterface:28.0.0'

在对应的 界面或者首界面添加动态权限申请

 /*** 动态申请  (电话/位置/存储)*/@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)private void requestPermission() {AndPermission.with(this).permission(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE).rationale(new Rationale() {@Overridepublic void showRationale(Context context, List<String> permissions, RequestExecutor executor) {executor.execute();}}).onGranted(new Action() {@Overridepublic void onAction(List<String> permissions) {Log.e(TAG, "用户给权限");}}).onDenied(new Action() {@Overridepublic void onAction(List<String> permissions) {if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) {// 打开权限设置页AndPermission.permissionSetting(MainActivity.this).execute();return;}Log.e(TAG, "用户拒绝权限");}}).start();}

2.2 添加布局

布局中包含的信息主要有 SurfaceView 、拍照button 、闪光灯按钮及切换镜头按钮

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/black"tools:context="cn.tongue.tonguecamera.ui.CameraActivity"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><SurfaceViewandroid:id="@+id/surfaceView"android:layout_width="match_parent"android:layout_height="wrap_content" /></FrameLayout><RelativeLayoutandroid:id="@+id/homecamera_bottom_relative"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#00ffffff"android:layout_alignParentBottom="true">// 返回按钮<ImageViewandroid:id="@+id/iv_back"android:layout_width="40dp"android:layout_height="30dp"android:scaleType="centerInside"android:layout_marginBottom="20dp"android:layout_marginStart="20dp"android:layout_centerVertical="true"android:background="@drawable/icon_back" />// 拍照<ImageViewandroid:id="@+id/img_camera"android:layout_width="80dp"android:layout_height="80dp"android:scaleType="centerInside"android:layout_marginBottom="20dp"android:layout_centerInParent="true"android:background="@drawable/camera" /></RelativeLayout><LinearLayoutandroid:id="@+id/home_custom_top_relative"android:layout_width="match_parent"android:layout_height="50dp"android:gravity="center_vertical"android:orientation="horizontal"android:background="#00ffffff"android:layout_alignParentTop="true">// 切换闪光灯<ImageViewandroid:id="@+id/camera_flash"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:padding="10dp"android:src="@drawable/icon_camera_off" /><Viewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="5"/>// 前/后 镜头<ImageViewandroid:id="@+id/camera_switch"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:padding="10dp"android:src="@drawable/btn_camera_turn_n" /></LinearLayout></RelativeLayout>

2.3 代码设置camera

在添加完拍照所需要的权限和布局后,我们就可以在Activity中书写对应的 逻辑和声明了。

2.3.1 通过 SurfaceView 获取需要数据

 // 部分对象的声明private Camera mCamera;private SurfaceHolder mHolder;mHolder = svContent.getHolder();mHolder.addCallback(this);// SurfaceHolder 的监听事件@Overridepublic void surfaceCreated(SurfaceHolder holder) {// 相机预览startPreview(mCamera, holder);}// 画布改变 调用 相机预览@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {mCamera.stopPreview();startPreview(mCamera, holder);}// 画布销毁  回收相机@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {releaseCamera();}/*** 预览相机*/private void startPreview(Camera camera, SurfaceHolder holder) {try {// 确认相机预览尺寸setupCamera(camera);camera.setPreviewDisplay(holder);cameraInstance.setCameraDisplayOrientation(this, mCameraId, camera);camera.startPreview();isView = true;} catch (IOException e) {e.printStackTrace();}}/*** 设置surfaceView的尺寸 因为camera默认是横屏,所以取得支持尺寸也都是横屏的尺寸* 我们在startPreview方法里面把它矫正了过来,但是这里我们设置设置surfaceView的尺寸的时候要注意 previewSize.height<previewSize.width* previewSize.width才是surfaceView的高度* 一般相机都是屏幕的宽度 这里设置为屏幕宽度 高度自适应 你也可以设置自己想要的大小*/private void setupCamera(Camera camera) {Camera.Parameters parameters = camera.getParameters();if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);}//根据屏幕尺寸获取最佳 大小Camera.Size previewSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPreviewSizes(),screenHeight, screenWidth);parameters.setPreviewSize(previewSize.width, previewSize.height);Camera.Size pictrueSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPictureSizes(),screenHeight,screenWidth);parameters.setPictureSize(pictrueSize.width, pictrueSize.height);camera.setParameters(parameters);// picHeight = (screenWidth * pictrueSize.width) / pictrueSize.height;picWidth = pictrueSize.width;picHeight = pictrueSize.height;FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(screenWidth,(screenWidth * pictrueSize.width) / pictrueSize.height);svContent.setLayoutParams(params);}

2.3.2 打开相机

为了避免界面切换 相机界面出现暂停情况,我们在onResume()方法中调用相机的获取和相机的预览操作。

 @Overrideprotected void onResume() {super.onResume();if (mCamera == null) {mCamera = getCamera(mCameraId);if (mHolder != null) {startPreview(mCamera, mHolder);}}}/*** 获取Camera实例** @return Camera*/private Camera getCamera(int id) {Camera camera = null;try {camera = Camera.open(id);} catch (Exception e) {Log.e(TAG, "getCamera: " + e);}return camera;}

2.3.3 释放相机

  /*** 释放相机资源*/private void releaseCamera() {if (mCamera != null) {mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.release();mCamera = null;}}
2.3.4 切换闪光灯模式

Camera.Parameters.FLASH_MODE_AUTO 自动模式,当光线较暗时自动打开闪光灯;

Camera.Parameters.FLASH_MODE_OFF 关闭闪光灯;

Camera.Parameters.FLASH_MODE_ON 拍照时闪光灯;

Camera.Parameters.FLASH_MODE_RED_EYE 闪光灯参数,防红眼模式。

 /*** 自动模式闪光灯** @param mCamera mCamera*/Camera.Parameters parameters = mCamera.getParameters();parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);//开启mCamera.setParameters(parameters);/*** 关闭闪光灯** @param mCamera mCamera*/Camera.Parameters parameters = mCamera.getParameters();parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);mCamera.setParameters(parameters);/*** 打开闪关灯** @param mCamera mCamera*/Camera.Parameters parameters = mCamera.getParameters();parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//开启
2.3.5 切换闪光灯模式
 /*** 切换前后摄像头*/public void switchCamera() {releaseCamera();mCameraId = (mCameraId + 1) % Camera.getNumberOfCameras();mCamera = getCamera(mCameraId);if (mHolder != null) {startPreview(mCamera, mHolder);}}

2.3.6 部分配置参数

对焦模式配置参数,可以通过Parameters.getFocusMode()接口获取。

Camera.Parameters.FOCUS_MODE_AUTO 自动对焦模式,摄影小白专用模式;

Camera.Parameters.FOCUS_MODE_FIXED 固定焦距模式,拍摄老司机模式;

Camera.Parameters.FOCUS_MODE_EDOF 景深模式,文艺女青年最喜欢的模式;

Camera.Parameters.FOCUS_MODE_INFINITY 远景模式,拍风景大场面的模式;

Camera.Parameters.FOCUS_MODE_MACRO 微焦模式,拍摄小花小草小蚂蚁专用模式;

场景模式配置参数,可以通过Parameters.getSceneMode()接口获取。

Camera.Parameters.SCENE_MODE_BARCODE 扫描条码场景,NextQRCode项目会判断并设置为这个场景;

Camera.Parameters.SCENE_MODE_ACTION 动作场景,就是抓拍跑得飞快的运动员、汽车等场景用的;

Camera.Parameters.SCENE_MODE_AUTO 自动选择场景;

Camera.Parameters.SCENE_MODE_HDR 高动态对比度场景,通常用于拍摄晚霞等明暗分明的照片;

Camera.Parameters.SCENE_MODE_NIGHT 夜间场景;

2.3.7 整体的Activity代码

接下来 我会将整体的Activity代码都放上来。

CameraActivity

package cn.tongue.tonguecamera.ui;import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;import java.io.File;
import java.io.IOException;import butterknife.BindView;
import butterknife.OnClick;
import cn.tongue.tonguecamera.R;
import cn.tongue.tonguecamera.base.BaseActivity;
import cn.tongue.tonguecamera.util.AppConstant;
import cn.tongue.tonguecamera.util.BitmapUtils;
import cn.tongue.tonguecamera.util.CameraUtil;/*** 拍照界面* 5.0 版本以前的拍照** @author ymc*/public class CameraActivity extends BaseActivity implements SurfaceHolder.Callback {private static final String TAG = "CameraActivity";@BindView(R.id.surfaceView)SurfaceView svContent;@BindView(R.id.img_camera)ImageView ivCamera;@BindView(R.id.camera_flash)ImageView ivFlash;@BindView(R.id.camera_switch)ImageView ivSwitch;@BindView(R.id.iv_back)ImageView ivBack;private Camera mCamera;private SurfaceHolder mHolder;private CameraUtil cameraInstance;/*** 屏幕宽高*/private int screenWidth;private int screenHeight;/*** 图片宽高*/private int picWidth;/*** 是否有界面*/private boolean isView = true;/*** 拍照id  1: 前摄像头  0:后摄像头*/private int mCameraId = 0;/*** 闪光灯类型 0 :关闭 1: 打开 2:自动*/private int light_type = 0;/*** 图片高度*/private int picHeight;@Overrideprotected int getLayoutId() {return R.layout.activity_camera;}@Overrideprotected void initView() {mHolder = svContent.getHolder();mHolder.addCallback(this);}@Overrideprotected void initData() {cameraInstance = CameraUtil.getInstance();DisplayMetrics dm = getResources().getDisplayMetrics();screenWidth = dm.widthPixels;screenHeight = dm.heightPixels;}@Overrideprotected void onResume() {super.onResume();if (mCamera == null) {mCamera = getCamera(mCameraId);if (mHolder != null) {startPreview(mCamera, mHolder);}}}@OnClick({R.id.img_camera, R.id.camera_flash, R.id.camera_switch, R.id.iv_back})public void OnClick(View view) {switch (view.getId()) {// 点击拍照case R.id.img_camera:switch (light_type) {case 0://关闭cameraInstance.turnLightOff(mCamera);break;case 1:cameraInstance.turnLightOn(mCamera);break;case 2://自动cameraInstance.turnLightAuto(mCamera);break;default:break;}takePhoto();break;// 切换闪光灯case R.id.camera_flash:if (mCameraId == 1) {Toast.makeText(this, "请切换到后置摄像头", Toast.LENGTH_LONG).show();return;}Camera.Parameters parameters = mCamera.getParameters();switch (light_type) {case 0://打开light_type = 1;ivFlash.setImageResource(R.drawable.icon_camera_on);parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//开启mCamera.setParameters(parameters);break;case 1://自动light_type = 2;parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);mCamera.setParameters(parameters);ivFlash.setImageResource(R.drawable.icon_camera_a);break;case 2://关闭light_type = 0;//关闭parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);mCamera.setParameters(parameters);ivFlash.setImageResource(R.drawable.icon_camera_off);break;default:break;}break;//切换前后摄像头case R.id.camera_switch:switchCamera();break;// 返回按钮case R.id.iv_back:finish();break;default:break;}}/*** 切换前后摄像头*/public void switchCamera() {releaseCamera();mCameraId = (mCameraId + 1) % Camera.getNumberOfCameras();mCamera = getCamera(mCameraId);if (mHolder != null) {startPreview(mCamera, mHolder);}}/*** 拍照*/private void takePhoto() {mCamera.takePicture(null, null, new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {isView = false;//将data 转换为位图 或者你也可以直接保存为文件使用 FileOutputStream//这里我相信大部分都有其他用处把 比如加个水印 后续再讲解Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);Bitmap saveBitmap = cameraInstance.setTakePicktrueOrientation(mCameraId, bitmap);saveBitmap = Bitmap.createScaledBitmap(saveBitmap, screenWidth, screenHeight, true);String imgpath = getExternalFilesDir(Environment.DIRECTORY_DCIM).getPath() +File.separator + System.currentTimeMillis() + ".jpeg";Log.e(TAG, "imgpath: ---  " + imgpath);BitmapUtils.saveJPGE_After(getApplicationContext(), saveBitmap, imgpath, 100);if (!bitmap.isRecycled()) {bitmap.recycle();}if (!saveBitmap.isRecycled()) {saveBitmap.recycle();}Intent intent = new Intent();intent.putExtra(AppConstant.KEY.IMG_PATH, imgpath);intent.putExtra(AppConstant.KEY.PIC_WIDTH, picWidth);intent.putExtra(AppConstant.KEY.PIC_HEIGHT, picHeight);setResult(AppConstant.RESULT_CODE.RESULT_OK, intent);finish();}});}@Overridepublic void surfaceCreated(SurfaceHolder holder) {startPreview(mCamera, holder);}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {mCamera.stopPreview();startPreview(mCamera, holder);}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {releaseCamera();}/*** 释放相机资源*/private void releaseCamera() {if (mCamera != null) {mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.release();mCamera = null;}}/*** 预览相机*/private void startPreview(Camera camera, SurfaceHolder holder) {try {setupCamera(camera);camera.setPreviewDisplay(holder);cameraInstance.setCameraDisplayOrientation(this, mCameraId, camera);camera.startPreview();isView = true;} catch (IOException e) {e.printStackTrace();}}/*** 设置surfaceView的尺寸 因为camera默认是横屏,所以取得支持尺寸也都是横屏的尺寸* 我们在startPreview方法里面把它矫正了过来,但是这里我们设置设置surfaceView的尺寸的时候要注意 previewSize.height<previewSize.width* previewSize.width才是surfaceView的高度* 一般相机都是屏幕的宽度 这里设置为屏幕宽度 高度自适应 你也可以设置自己想要的大小*/private void setupCamera(Camera camera) {Camera.Parameters parameters = camera.getParameters();if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);}//根据屏幕尺寸获取最佳 大小Camera.Size previewSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPreviewSizes(),screenHeight, screenWidth);parameters.setPreviewSize(previewSize.width, previewSize.height);Camera.Size pictrueSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPictureSizes(),screenHeight,screenWidth);parameters.setPictureSize(pictrueSize.width, pictrueSize.height);camera.setParameters(parameters);
//        picHeight = (screenWidth * pictrueSize.width) / pictrueSize.height;picWidth = pictrueSize.width;picHeight = pictrueSize.height;FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(screenWidth,(screenWidth * pictrueSize.width) / pictrueSize.height);svContent.setLayoutParams(params);}/*** 获取Camera实例** @return Camera*/private Camera getCamera(int id) {Camera camera = null;try {camera = Camera.open(id);} catch (Exception e) {Log.e(TAG, "getCamera: " + e);}return camera;}}

CameraUtil

package cn.tongue.tonguecamera.util;import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Surface;import java.util.Collections;
import java.util.Comparator;
import java.util.List;/*** 拍照工具类*/
public class CameraUtil {private static final String TAG = "CameraUtil";/*** 降序*/private CameraDropSizeComparator dropSizeComparator = new CameraDropSizeComparator();/*** 升序*/private CameraAscendSizeComparator ascendSizeComparator = new CameraAscendSizeComparator();private static CameraUtil instance = null;private CameraUtil() {}public static CameraUtil getInstance() {if (instance == null) {instance = new CameraUtil();return instance;} else {return instance;}}private int getRecorderRotation(int cameraId) {android.hardware.Camera.CameraInfo info =new android.hardware.Camera.CameraInfo();android.hardware.Camera.getCameraInfo(cameraId, info);return info.orientation;}/*** 获取所有支持的返回视频尺寸** @param list      list* @param minHeight minHeight* @return Size*/private Size getPropVideoSize(List<Size> list, int minHeight) {Collections.sort(list, ascendSizeComparator);int i = 0;for (Size s : list) {if ((s.height >= minHeight)) {break;}i++;}if (i == list.size()) {i = 0;}return list.get(i);}/*** 保证预览方向正确** @param activity activity* @param cameraId cameraId* @param camera   camera*/public void setCameraDisplayOrientation(Activity activity,int cameraId, Camera camera) {android.hardware.Camera.CameraInfo info =new android.hardware.Camera.CameraInfo();android.hardware.Camera.getCameraInfo(cameraId, 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;default: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;}//设置角度camera.setDisplayOrientation(result);}public Bitmap setTakePicktrueOrientation(int id, Bitmap bitmap) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(id, info);bitmap = rotaingImageView(id, info.orientation, bitmap);return bitmap;}/*** 把相机拍照返回照片转正** @param angle 旋转角度* @return bitmap 图片*/private Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {//矩阵Matrix matrix = new Matrix();matrix.postRotate(angle);//加入翻转 把相机拍照返回照片转正if (id == 1) {matrix.postScale(-1, 1);}// 创建新的图片Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,bitmap.getWidth(), bitmap.getHeight(), matrix, true);return resizedBitmap;}/*** 获取所有支持的预览尺寸** @param list     list* @param minWidth minWidth* @return Size*/private Size getPropPreviewSize(List<Size> list, int minWidth) {Collections.sort(list, ascendSizeComparator);int i = 0;for (Size s : list) {if ((s.width >= minWidth)) {break;}i++;}if (i == list.size()) {i = 0;}return list.get(i);}/*** 获取所有支持的返回图片尺寸** @param list     list* @param minWidth minWidth* @return Size*/private Size getPropPictureSize(List<Size> list, int minWidth) {Collections.sort(list, ascendSizeComparator);int i = 0;for (Size s : list) {if ((s.width >= minWidth)) {break;}i++;}if (i == list.size()) {i = 0;}return list.get(i);}/*** 获取所有支持的返回视频尺寸** @param list      list* @param minHeight minHeight* @return Size*/public Size getPropSizeForHeight(List<Size> list, int minHeight) {Collections.sort(list, ascendSizeComparator);int i = 0;for (Size s : list) {if ((s.height >= minHeight)) {Log.e(TAG, "getPropSizeForHeight: s.height=" + s.height);break;}i++;}if (i == list.size()) {i = list.size();}return list.get(i);}/*** 根据 宽度和高度找到是否有相等的 尺寸  如果没有 就获取最小的 值* @param list list* @param th 高度* @param minWidth 宽度* @return size*/public  Size getPicPreviewSize(List<Camera.Size> list, int th, int minWidth){Collections.sort(list, ascendSizeComparator);int i = 0;for(int x=0;x<list.size();x++){Size s = list.get(x);// camera 中的宽度和高度 相反 因为测试板子原因 这里暂时 替换 && 为 ||if((s.width == th) && (s.height == minWidth)){i = x;break;}}//如果没找到,就选最小的size 0return list.get(i);}public Size getPropPictureSize(List<Camera.Size> list, float th, int minWidth){Collections.sort(list, ascendSizeComparator);int i = 0;for(Size s:list){if((s.width >= minWidth) && equalRate(s, th)){Log.i(TAG, "PictureSize : w = " + s.width + "h = " + s.height);break;}i++;}if(i == list.size()){i = 0;//如果没找到,就选最小的size}return list.get(i);}/*** 升序 按照高度*/private class CameraAscendSizeComparatorForHeight implements Comparator<Size> {@Overridepublic int compare(Size lhs, Size rhs) {if (lhs.height == rhs.height) {return 0;} else if (lhs.height > rhs.height) {return 1;} else {return -1;}}}private boolean equalRate(Size s, float rate) {float r = (float) (s.width) / (float) (s.height);return Math.abs(r - rate) <= 0.03;}/*** 降序*/private class CameraDropSizeComparator implements Comparator<Size> {@Overridepublic int compare(Size lhs, Size rhs) {if (lhs.width == rhs.width) {return 0;} else if (lhs.width < rhs.width) {return 1;} else {return -1;}}}/*** 升序*/private class CameraAscendSizeComparator implements Comparator<Size> {@Overridepublic int compare(Size lhs, Size rhs) {if (lhs.width == rhs.width) {return 0;} else if (lhs.width > rhs.width) {return 1;} else {return -1;}}}/*** 打印支持的previewSizes** @param params*/private void printSupportPreviewSize(Camera.Parameters params) {List<Size> previewSizes = params.getSupportedPreviewSizes();for (int i = 0; i < previewSizes.size(); i++) {Size size = previewSizes.get(i);}}/*** 打印支持的pictureSizes** @param params*/private void printSupportPictureSize(Camera.Parameters params) {List<Size> pictureSizes = params.getSupportedPictureSizes();for (int i = 0; i < pictureSizes.size(); i++) {Size size = pictureSizes.get(i);}}/*** 打印支持的聚焦模式** @param params params*/private void printSupportFocusMode(Camera.Parameters params) {List<String> focusModes = params.getSupportedFocusModes();for (String mode : focusModes) {Log.e(TAG, "printSupportFocusMode: " + mode);}}/*** 打开闪关灯** @param mCamera mCamera*/public void turnLightOn(Camera mCamera) {if (mCamera == null) {return;}Camera.Parameters parameters = mCamera.getParameters();if (parameters == null) {return;}List<String> flashModes = parameters.getSupportedFlashModes();if (flashModes == null) {return;}String flashMode = parameters.getFlashMode();if (!Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {// Turn on the flashif (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);mCamera.setParameters(parameters);}}}/*** 自动模式闪光灯** @param mCamera mCamera*/public void turnLightAuto(Camera mCamera) {if (mCamera == null) {return;}Camera.Parameters parameters = mCamera.getParameters();if (parameters == null) {return;}List<String> flashModes = parameters.getSupportedFlashModes();if (flashModes == null) {return;}String flashMode = parameters.getFlashMode();if (!Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)) {// Turn on the flashif (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);mCamera.setParameters(parameters);}}}/*** 关闭闪光灯** @param mCamera mCamera*/public void turnLightOff(Camera mCamera) {if (mCamera == null) {return;}Camera.Parameters parameters = mCamera.getParameters();if (parameters == null) {return;}List<String> flashModes = parameters.getSupportedFlashModes();String flashMode = parameters.getFlashMode();if (flashModes == null) {return;}if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);mCamera.setParameters(parameters);}}}
}

如果想要更加详细的了解 camera + surfaceview 请移步博客顶部GitHub地址内 clone project 运行即可

Android自定义camera相机 系列(一)相关推荐

  1. Android 自定义Camera相机(封装工具类贼简单)

    背景 目前公司的项目都是,针对生物认证来进行一些验证之类的功能,比方说,交互式活体检测,人脸1v1(对比),人脸1vN(搜索).用系统自带的相机?当然是不够用了,不够用那就自定义啦,就像是前几天七夕, ...

  2. Android自定义Camera2相机

    转载请标明出处:https://blog.csdn.net/ZhijunHong/article/details/115730693,谢谢~ 写在前面 Google从Android 5.0 L(API ...

  3. Android 系统(201)---Android 自定义View实战系列 :时间轴

    Android 自定义View实战系列 :时间轴 Android开发中,时间轴的 UI需求非常常见,如下图: 本文将结合 自定义View & RecyclerView的知识,手把手教你实现该常 ...

  4. Android 自定义Camera(一)如何预览相机

    Android Camera之如何预览相机 1.官方Api描述 翻译后为: 使用android.hardware.Camera拍照,请使用以下步骤: 1.从open(int)获取一个Camera实例. ...

  5. Android之 Camera相机使用

    一 简介 1.1 随着信息时代的发展,相机在我们生活中使用越来越频繁,也成为手机的基本配置之一.相机可以用来拍照,拍视频,人脸识别,视频聊天,扫码支付,监控等常见领域 不管什么场景,基本原理都差不多, ...

  6. android自定义美颜相机完整程序,Android OpenGL ES从入门到进阶(一)—— 五分钟开发一款美颜相机...

    源码链接:https://github.com/smzhldr/AGLFramework 一.前言 商店里有数十款的美颜相机类产品,其实现原理基本上都是以OpenGL ES为核心的特效处理,大神可以忽 ...

  7. android自定义camera预览区域,android camera摄像surfaceview预览界面特定区域(该区域可移动)...

    1.自定义一个imageview用来设定surfaceview上的特定区域. public class DrawImageView extends ImageView { private Paint ...

  8. android自定义美颜相机,效果最自然 美颜相机for Android版更新

    [IT168 资讯]备受爱自拍女生追捧的手机自拍神器"美颜相机"安卓版在八月初迎来了又一次重大改版,跟iPhone最新版一样,美颜相机安卓版1.3也在"自拍"功 ...

  9. Android Camera开发系列(下)——自定义Camera实现拍照查看图片等功能

    Android Camera开发系列(下)--自定义Camera实现拍照查看图片等功能 Android Camera开发系列(上)--Camera的基本调用与实现拍照功能以及获取拍照图片加载大图片 上 ...

最新文章

  1. 意念实时转语音!Facebook的非植入式脑机接口,解码准确率达到76%
  2. CMake命令之list
  3. 投资学习网课笔记(part1)--基金第一课
  4. UVA 12904 Load Balancing 暴力
  5. 项目学习类容,日期处理,SQL语句查询,hashtable的运用,R语言包的环境配置
  6. 详解 YUV 格式(I420/YUV420/NV12/NV12/YUV422)
  7. 如何解决您的虚拟主机中有文件触发了安全防护报警规则,可能存在webshell网页木马...
  8. 三维点云:学习+了解
  9. 《Linux设备驱动开发详解 A》一一3.1 Linux内核的发展与演变
  10. excel怎么一个格子斜分_表格excel怎样把一格用斜线分为三格
  11. INT_MAX和INT_MIN的含义和用法
  12. 4种最常用的诡辩方法
  13. 机器学习之从基础数学深入剖析逻辑回归(案例理论相结合)
  14. 寄生电容和分布电容的区别
  15. EXCEL2016设置下拉选项,图文说明
  16. 对计算机专业最难的,计算机专业: 最好的7所大学! 也是全中国“最难考”的大学!...
  17. face.evoLVe 人脸比对测试
  18. 全面理解-Flutter(万字长文,【性能优化实战】
  19. 支持Apple pay支付的设备
  20. 传统软件行业与互联网行业对比

热门文章

  1. ORA-27090: Unable to reserve kernel resources for asynchronous disk I/O
  2. ElasticSearch 全文搜索引擎的查询详解①(Ubuntu版 v6.6.2)
  3. Myeclipse 10激活失败解决方案
  4. 大学生,就业or择业?
  5. 客观赋权法——CRITIC权重法【Python实现】
  6. 打开excel文件的密码忘记了
  7. 同是软件测试,你年薪百万,我年薪10万,真的差距这么大吗?
  8. Mybatis:动态SQL分组查询
  9. EZView/智U客户端设备列表中的EZView_Demo删除后如何恢复
  10. (三)vivado硬件调试ILA的使用