[Android 相机]Android 相机开发的基本流程

https://blog.csdn.net/bluewindtalker/article/details/54563910

相机开发现在有2个类,分别为android.hardware.camera2和android.hardware.Camera,其中Camera类官方已经不推荐,不过鉴于有前人踩坑了,为了快速开发也就直接拿来用了

This class was deprecated in API level 21.
We recommend using the new android.hardware.camera2 API for new applications.

关于旧版的Camera类,google官方给了下面的指导步骤,https://developer.android.com/reference/android/hardware/Camera.html

To take pictures with this class, use the following steps:

看了这些后我们可以简单的进行实战,首先是初始化camera的过程。

[java] view plaincopy
  1. /**
  2. * 初始化照片
  3. */
  4. private void initCamera() {
  5. if (camera != null) {
  6. camera.startPreview();
  7. }
  8. Log.e(TAG, "initCamera");
  9. //1. Obtain an instance of Camera from open(int).
  10. //这里可以根据前后摄像头设置
  11. camera = openCamera(currentCameraType);
  12. if (camera == null) {
  13. return;
  14. }
  15. //2. Get existing (default) settings with getParameters().
  16. //获得存在的默认配置属性
  17. Camera.Parameters parameters = camera.getParameters();
  18. //3. If necessary, modify the returned Camera.Parameters object and call setParameters(Camera.Parameters).
  19. //可以根据需要修改属性,这些属性包括是否自动持续对焦、拍摄的gps信息、图片视频格式及大小、预览的fps、
  20. // 白平衡和自动曝光补偿、自动对焦区域、闪光灯状态等。
  21. //具体可以参阅https://developer.android.com/reference/android/hardware/Camera.Parameters.html
  22. if (parameters.getSupportedFocusModes().contains(Camera.Parameters
  23. .FOCUS_MODE_CONTINUOUS_PICTURE)) {
  24. //自动持续对焦
  25. parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
  26. }
  27. //在设置图片和预览的大小时要注意当前摄像头支持的大小,不同手机支持的大小不同,如果你的SurfaceView不是全屏,有可能被拉伸。
  28. // parameters.getSupportedPreviewSizes(),parameters.getSupportedPictureSizes()
  29. List<Camera.Size> picSizes = parameters.getSupportedPictureSizes();
  30. Resources resources = this.getResources();
  31. DisplayMetrics dm = resources.getDisplayMetrics();
  32. float density = dm.density;
  33. int width = dm.widthPixels;
  34. int height = dm.heightPixels;
  35. Camera.Size picSize = getPictureSize(picSizes, width, height);
  36. parameters.setPictureSize(picSize.width, picSize.height);
  37. camera.setParameters(parameters);
  38. //4. Call setDisplayOrientation(int) to ensure correct orientation of preview.
  39. //你可能会遇到画面方向和手机的方向不一致的问题,竖向手机的时候,但是画面是横的,这是由于摄像头默认捕获的画面横向的
  40. // 通过调用setDisplayOrientation来设置PreviewDisplay的方向,可以解决这个问题。
  41. setCameraDisplayOrientation(this, currentCameraType, camera);
  42. //5. Important: Pass a fully initialized SurfaceHolder to setPreviewDisplay(SurfaceHolder).
  43. // Without a surface, the camera will be unable to start the preview.
  44. //camera必须绑定一个surfaceview才可以正常显示。
  45. try {
  46. camera.setPreviewDisplay(displaySfv.getHolder());
  47. } catch (IOException e) {
  48. e.printStackTrace();
  49. }
  50. //6. Important: Call startPreview() to start updating the preview surface.
  51. // Preview must be started before you can take a picture.
  52. //在调用拍照之前必须调用startPreview()方法,但是在此时有可能surface还未创建成功。
  53. // 所以加上SurfaceHolder.Callback(),在回调再次初始化下。
  54. camera.startPreview();
  55. //7. When you want, call
  56. // takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)
  57. // to capture a photo. Wait for the callbacks to provide the actual image data.
  58. //当如果想要拍照的时候,调用takePicture方法,这个下面我们会讲到。
  59. //8. After taking a picture, preview display will have stopped. To take more photos, call startPreview() again first.
  60. //在拍照结束后相机预览将会关闭,如果要再次拍照需要再次调用startPreview()
  61. //9. Call stopPreview() to stop updating the preview surface.
  62. //通过调用stopPreview方法可以结束预览
  63. //10. Important: Call release() to release the camera for use by other applications.
  64. // Applications should release the camera immediately in onPause()(and re-open() it in onResume()).
  65. //建议在onResume调用open的方法,在onPause的时候执行release方法
  66. }

根据上文提到的第9、10步骤我们在onResume与onPause做处理。

[java] view plaincopy
  1. @Override
  2. protected void onResume() {
  3. super.onResume();
  4. Log.e(TAG, "onResume");
  5. if (!isRequestPermission) {
  6. checkAndInitCamera();
  7. }
  8. }
  9. @Override
  10. protected void onPause() {
  11. super.onPause();
  12. Log.e(TAG, "onPause");
  13. releaseCamera();
  14. }
  15. private void releaseCamera() {
  16. if (camera != null) {
  17. camera.stopPreview();
  18. camera.release();
  19. camera = null;
  20. }
  21. }

其中checkAndInitCamera()为权限处理的方法

[java] view plaincopy
  1. private void checkAndInitCamera() {
  2. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  3. // 检查该权限是否已经获取
  4. int i = ContextCompat.checkSelfPermission(this, permissions[0]);
  5. // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝
  6. if (i != PackageManager.PERMISSION_GRANTED) {
  7. // 如果没有授予该权限,就去提示用户请求
  8. isRequestPermission = true;
  9. ActivityCompat.requestPermissions(this, permissions, CAMERA_PERMISSION_CODE);
  10. } else {
  11. initCamera();
  12. }
  13. } else {
  14. initCamera();
  15. }
  16. }

这里有些细节需要注意,在parameters.setPictureSize(int width, int height);这个方法的时候
不能将宽高随意写,必须从parameters.getSupportedPictureSizes();中选择最合适的宽高,否则会出现setParameters failed的运行时错误。

而系统提供的宽高是根据摄像头的参数定的,这个导致需要根据手机和surfaceview宽高来动态适配,否则可能会出现图像失真拉伸压缩的情况,在本文中将直接使用最接近的摄像头像素的算法
[java] view plaincopy
  1. /**
  2. * 获得最合是的宽高size
  3. */
  4. private Camera.Size getPictureSize(List<Camera.Size> picSizes, int width, int height) {
  5. Camera.Size betterSize = null;
  6. int diff = Integer.MAX_VALUE;
  7. if (picSizes != null && picSizes.size() > 0) {
  8. for (Camera.Size size : picSizes) {
  9. int newDiff = Math.abs(size.width - width) + Math.abs(size.height - height);
  10. if(newDiff == 0){
  11. return size;
  12. }
  13. if (newDiff < diff) {
  14. betterSize = size;
  15. diff = newDiff;
  16. }
  17. }
  18. }
  19. return betterSize;
  20. }
还有一个细节是摄像头并不是正的,调用的方法

setCameraDisplayOrientation

[java] view plaincopy
  1. //设置相机的方向
  2. public int setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
  3. android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
  4. android.hardware.Camera.getCameraInfo(cameraId, info);
  5. int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
  6. int degrees = 0;
  7. switch (rotation) {
  8. case Surface.ROTATION_0:
  9. degrees = 0;
  10. break;
  11. case Surface.ROTATION_90:
  12. degrees = 90;
  13. break;
  14. case Surface.ROTATION_180:
  15. degrees = 180;
  16. break;
  17. case Surface.ROTATION_270:
  18. degrees = 270;
  19. break;
  20. default:
  21. degrees = 0;
  22. break;
  23. }
  24. int result;
  25. if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
  26. result = (info.orientation + degrees) % 360;
  27. result = (360 - result) % 360;   // compensate the mirror
  28. } else {
  29. // back-facing
  30. result = (info.orientation - degrees + 360) % 360;
  31. }
  32. camera.setDisplayOrientation(result);
  33. return degrees;
  34. }

https://blog.csdn.net/bluewindtalker/article/details/54563910

这么做运行的时候,我们发现预览图并不能正常显示出来,这是因为surface还没有正常创建出来,这时候我们可以在initCamera方法中加入如下代码,坚挺SurfaceHolder的事件回调

[java] view plaincopy
  1. SurfaceHolder holder = displaySfv.getHolder();
  2. if (holder != null) {
  3. holder.addCallback(new SurfaceHolder.Callback() {
  4. @Override
  5. public void surfaceCreated(SurfaceHolder holder) {
  6. Log.e(TAG, "surfaceCreated" + holder);
  7. checkAndInitCamera();
  8. }
  9. @Override
  10. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  11. Log.e(TAG, "surfaceChanged" + holder);
  12. }
  13. @Override
  14. public void surfaceDestroyed(SurfaceHolder holder) {
  15. Log.e(TAG, "surfaceDestroyed" + holder);
  16. }
  17. });
  18. }

以下就是重点了,拍照,拍照就是触发一个回调事件方法。

[java] view plaincopy
  1. /**
  2. * 拍摄照片
  3. */
  4. private void takePicture() {
  5. picIV.setImageBitmap(null);
  6. if (camera == null) {
  7. return;
  8. }
  9. //如果不加第一个回调,手机会没有拍照音效,第二个回调是返回raw格式图片,
  10. // 了解过相机的人可能知道这是原图的意思,这个我们不处理,我们处理第三个回调,jpg格式的数据
  11. // 拍摄照片
  12. camera.takePicture(new Camera.ShutterCallback() {
  13. @Override
  14. public void onShutter() {
  15. }
  16. }, null, new Camera.PictureCallback() {
  17. @Override
  18. public void onPictureTaken(byte[] data, Camera camera) {
  19. // 将拍照数据data数组转化为Bitmap,这里应该放到线程执行了,这里为了简单处理直接放UI线程了
  20. Bitmap imageBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
  21. //一般手机需要旋转90度来适应方向,如果setCameraDisplayOrientation得到的结果不是90度,一般还需要再次旋转180
  22. picIV.setImageBitmap(rotate(imageBitmap, 90));
  23. picFl.setVisibility(View.VISIBLE);
  24. }
  25. });
  26. }
  27. public Bitmap rotate(Bitmap bitmap, int degree) {
  28. Matrix matrix = new Matrix();
  29. matrix.postRotate(degree);
  30. return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  31. }

以下是布局文件内容

[html] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context="com.bluewindtalker.camera.demo.CameraActivity">
  8. <SurfaceView
  9. android:id="@+id/sf_display_demo_camera"
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent" />
  12. <Button
  13. android:id="@+id/btn_take_picture_demo_camera"
  14. android:layout_width="150dp"
  15. android:layout_height="50dp"
  16. android:layout_gravity="center_horizontal|bottom"
  17. android:layout_marginBottom="70dp"
  18. android:background="@android:color/black"
  19. android:gravity="center"
  20. android:text="拍照"
  21. android:textColor="@android:color/white"
  22. android:textSize="20sp" />
  23. <FrameLayout
  24. android:id="@+id/fl_picture_demo_camera"
  25. android:layout_width="match_parent"
  26. android:layout_height="match_parent"
  27. android:background="@android:color/black"
  28. android:visibility="gone">
  29. <ImageView
  30. android:id="@+id/iv_picture_demo_camera"
  31. android:layout_width="match_parent"
  32. android:layout_height="match_parent"
  33. android:scaleType="fitCenter" />
  34. <TextView
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_gravity="center"
  38. android:background="#66666666"
  39. android:padding="20dp"
  40. android:text="点击任意区域关闭照片"
  41. android:textColor="@android:color/white"
  42. android:textSize="18sp" />
  43. </FrameLayout>
  44. </FrameLayout>

下篇将讲解如何通过摄像头识别周围环境光亮强度

[Android相机]光线传感器识别环境光亮强度

[Android相机]通过手机摄像头识别环境亮度

git地址

https://github.com/bluewindtalker/camerademo

camera (19)---Android 相机开发的基本流程相关推荐

  1. CAMERA(12)---[Android相机]光线传感器识别环境光亮强度

    [Android相机]光线传感器识别环境光亮强度 关于Android相机的开发流程可以看前一篇文章, [Android 相机]Android 相机开发的基本流程 https://blog.csdn.n ...

  2. android 相机编程,Android相机开发系列

    Android Camera Develop Series 简介 Android相机开发系列文章循序渐进,教你从一个没有任何功能的相机APP开始,逐步完善实现一般相机APP的各种功能,甚至还能拿来做图 ...

  3. Android相机开发和遇到的坑

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/53350551 文章出自:薛瑄的博客 在Android相机开发实际开发过程中遇到了不 ...

  4. Android相机开发

    文章目录 Android相机开发 申请权限 创建一个可以预览的界面 1.创建一个新工程 2.在新创建的工程中activity中布局文件 3.创建一个相机预览的view 继承SurfaceView 4. ...

  5. Android相机开发那些坑

    最近我负责开发了一个跟Android相机有关的需求,新功能允许用户使用手机摄像头,快速拍摄特定尺寸(1:1或3:4)的照片,并支持在拍摄出的照片上做贴纸相关的操作.由于之前没有接触过Android相机 ...

  6. Android相机开发详解(一)

    Android相机开发详解(一) 请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客) Android相机 ...

  7. android相机开发书籍,Android Camera 驱动开发入门必备知识有哪些?

    1:硬件相关:简单的数字电路要懂,看的懂电路图(这个很简单),能根据电路找到相应Camera硬件连接的GPIO,并根据芯片手册配置GPIO为相应的模式:比如GPIO模式,输入IO或输出IO:或配置成P ...

  8. Android相机开发: 触摸对焦,触摸测光,二指手势缩放

    转自此处 概述 本篇在(四)的基础上继续对相机APP的功能进行增强.触摸对焦,就是在屏幕上点击某个点,相机就以此点内容进行对焦,保证此点最清晰:触摸测光,就是在屏幕上点击某个点,相机调整曝光亮度,保证 ...

  9. Android 相机开发

    在android中应用相机功能,一般有两种:一种是直接调用系统相机,一种自己写的相机. 我将分别演示两种方式的使用: 第一种:是使用Intent跳转到系统相机,action为:android.medi ...

最新文章

  1. nginx代理响应报文体不全解决思路
  2. JAVA多线程之Runnable和Thread比较
  3. 面对 20 亿行代码,Google 如何管理?
  4. linux运维入门第一周的学习部分命令!
  5. C++学习-环境配置
  6. java无穷大 inf_java – 为什么浮点数无穷大,不像NaN,等于?
  7. 2018 开源分布式中间件 DBLE 年报
  8. jQuery对表格的操作
  9. 拓端tecdat|R语言分位数回归预测筛选有上升潜力的股票
  10. Vue+Webpack打造todo应用
  11. 线性表——顺序表基本知识以及基本操作
  12. [noip2013]货车运输
  13. 安卓抓包,为何总是 Tunnel to?
  14. 网易云音乐的亏损,是社区经济的通病?
  15. 英国认定人脸识别公司Clearview AI侵犯隐私:罚款750万英镑,删除英国居民信息...
  16. 给文本文件每一行加行号
  17. 习题 6.20 用指向指针的指针的方法对n个整数排序并输出。要求将排序单独写成一个函数。整数和n在主函数中输入。最后在主函数中输出。
  18. 16进制转Base64的实现原理及代码
  19. Google Maps和中国地图
  20. 【时间之外】软件开发工作交接你会做吗?

热门文章

  1. P1396 营救(并查集+二分)
  2. maven05----maven仓库
  3. icomoon.io生成字体图标
  4. salt 文件push使用方法
  5. Java中的 WeakReference 和 SoftReference
  6. C# .net基于Http实现web server(web服务)
  7. c#与api类型对照表
  8. [转] 在 Mac OS X 下编译 Objective-C 运行时
  9. javascript 模仿点击链接
  10. 利用UrlRewrite,asp.net动态生成htm页面(补充说明)