手撸的Android相机的官方文档(有自己的理解,翻译的可能有点不对)

   一.简介

        在撸项目和学习国外大佬撸代码的时候,看到了一个拍照的功能,觉得非常不错,看了源码。发现他的Camera是引用compile'com.commonsware.cwac:camera:0.6.12'
   ,它报了一个布局异常,说这个CameraView类不能被初始化看了代码没找出原因,百度也没搜到原因和解决方法。所以就索性去Gooogle官网去看了Camera的文档。顺便克服自己看英文文档就头疼的病吧。并且在这里把辛辛苦苦手敲的翻译笔记记一下。当自己不记得的时候,在回来回顾一下。然后在自己撸一个Camera类去返回到项目中去使用。接下来先贴出来官方的API文档的位置:官方Camera文档地址(我有翻译的不对的地方,大家可以跟我说,或者告诉我。)

   二.Camera的介绍

    1.   官方文档的翻译是这样的:Android框架包括对设备上各种摄像头和摄像头功能的支持,允许您在应用程序中捕获图片和视频。本文档讨论了一种快速、简单的图像和视频捕获方法,并概述了为用户创建自定义相机体验的高级方法。
   2. 我的理解是:可以通过这个解释来看,Camera是一个非常核心的类。他能够摄像和视频。那么需要做相应功能,都需要这个类。(给我自己看的)
   

   三.Camera相关的类

     1.android.hardware.camera2     (本人亲自测试,这个类找不到,只找到这个包名,我也不太清楚是为什么,可能我AS哪些地方配置不对,有大神可以留言告诉我)
           这个包是私有的API为了控制照相设备,它能够拍照或者是拍视频,当你正在构建一个拍照的应用(APP)

     2.Camera
       这个类是最老的并且被废弃的API为了控制照相设备(通过这里可以看出,Android原生API中还是用上面的那个camera2,并且camera2是Camera的升级(名义上的))

     3.SurfaceView
        这个类是用来提供一个动态的照相机给用户(也就是说,用户实质上看到的照相机就是由它提供的)

     4.MediaRecorder
         这个类是被用来去记录从相机中获取的视频

     5.Intent 
         一个Intent Action类型: MediaStore.ACTION_IMAGE_CAPTURE 或者 MediaStore.ACTION_VIDEO_CAPTURE 可以被用来去拍摄照片或者视频即使没有直接使用Camera类(从这里可以看出,可以直接唤起一个拍照的Actvity,可以推测就是android手机原生的拍照界面或者是原生的拍摄视频的界面)

     PS:这里是我导入的情况,这里我没发现这个类,只找到这个包。而且官方文档里说的功能介绍是Camera类,这个camera2没介绍。所以暂时不管了

     
     手撸的Android相机的官方文档(有自己的理解,翻译的可能有点不对)

     四.在Manifest中申明相应的权限

         Manifeset的相关声明:
            在开始使用Camera API 去完成你的应用之前,你应该确认你的manifest拥有恰当的声明去允许使用相机设备和其它相关联的特征
          1.Camera Permission:你的应用必须请求允许去使用相机设备
             <uses-permission android:name="android.permission.CAMERA" /> (这个好像是被例如了危险权限,必须要申明)

          注释:如果你正在使用相机通过引用一个存在的相机APP,你的应用不必去请求这个permission(就是如果通过Intent去唤起别的APP的拍照功能,就不必声明这个权限)

          2.Camera Features :你的应用必须也要申明相机部件的使用权限
          <uses-feature android:name="android.hardware.camera" />
          ps:这个非常多,它的声明大部分是对于一些设备的硬件的功能是否需要调用的。例如:有些手机带前后摄像头,有些手机只带一个。这个时候你就要选择性的申明这些东西是否都需要使用。其实本质上来说,这是对应用的一种保护,如果你的应用代码会调用一个前置摄像头,但是那个手机并没有前置摄像头,那么就BOOM了,你的APP立马
ShutDown。所以这个users-feature的文档还是有必要去了解一下。我在这里贴出它的链接地址(可喜可贺的是,这个users-feature文档是中文的):users-feature的文档地址


         3.Storage Permission: 如果你的应用保存相片或者视频到设备的外存,你也必须要申明这个manifest
         <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


         4.Audio Recording Permission: 为了使用视频设备记录audio,你的APP必须申明audio部件的许可
         <uses-permission android:name="android.permission.RECORD_AUDIO" />


          5.Location Permission: 如果你的应用标记图片伴随着GPS位置信息,你也必须要申请ACCESS_FINE_LOCATION,记住这个,如果你的APP标记了Android5.0版本或者更高
            你也需要声明你的APP使用GPS设备
           <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
                ...
            <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
            <uses-feature android:name="android.hardware.location.gps" />
         PS:如果连以上的都看不懂,那么我觉得你有必要把你的Android基础知识在看看。要明白manifest的作用,整个Android构架的方式,多去看看官方文档和Android包下的源码

   

     五.使用已存在的照相APP

       它的使用方式就是通过Intent设置相应的intent-filter去唤起相应的Activity,不过你不了解别人的源码,你根本唤起不了,除非你唤起的是自己的其它APP,官方有相应的链接与解释,我贴出相应的连接:拍照的链接    摄像的链接  
     ps:这个除非在公司,否则你一般不用做了解的。而且原理也很简单,基础的Android书也讲解了intent隐式唤起Activity

    六.构建一个照相APP(重点来了)

       一些开发者获取要求一个定制的相机使用界面去看他们的应用或者提供一个特殊的部件,写你自己的照相代码可以给你的使用者提供一个更引人入胜的外观
注释:以下的指导是为了这个更老或者已经被抛去的CameraAPI,为了最新的或者最先进的照相应用,这个最新的android.hardware.camera2 API被推荐使用
    ps:emmm,我前面贴出了camera2的问题,只找到了包,没找到这个类。。。。
     
   
     接下来的步骤为了创建一个定制的照相界面为了你的应用,是以下的这些:
        1.侦查和获取Camera: 创建代码去检查相机的存在和要求入口
        2.创建一个象征类:  创建一个相机象征类,继承SurfaceView并且实现SurfaceHolder接口,这个类预先观看这个动态的图片从相机中
        3.构建一个象征的布局(Layout): 一旦你有了相机象征类,创建一个view布局去具体化你想要的象征和界面控制
        4.建立监听器们为了捕捉: 连接监听器们为了你的界面控制去开始捕捉照相或者视频在用户的行为回调中,就像按下一个按钮
        5.捕捉和保存文件: 建立代码去捕捉照片和视频并且保存这些输出(即从硬件中返回的这些数据)
        6.释放Camera : 在使用Camera之后,你的应用必须恰当的释放它为了通过其它的应用可以使用


    相机硬件是一个被分享使用的来源,所以你必须小心的管理,所以你的应用不能够与其它想用相机的应用冲突。接下来的部分讨论的是怎样去侦查相机部件,怎样去
请求一个相机的入口,怎样去捕捉照片或者视频和怎样去解析Camera当你的引用完成使用了相机
     警告:记住释放相机类通过使用Camera.release(),当你的应用使用完相机!如果你的应用没有恰当的释放相机,所有随后意图去调用相机,包括哪些通过你的应用
的行为将会失败,并且可能会导致你和其他的应用被关闭
     

    七.侦测相机硬件

       如果你的应用并没有明确的请求一个相机通过一个manifest声明,你应该在运行的时候检查是否相机是可用的,去完成这个检查,通过使用
PackageManager.hasSystemFeature()方法,就像下面的例子代码展示的:
   private boolean checkCameraHardware(Context context){
    if(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
         return true;
     }else return false;
}
  注解:android 设备可能有多个的摄像机,例如一个背置物理摄像头和一个前置通话摄像头,android2.3和之后允许你检查这个在一个设备上的可获取的摄像头通过
使用Camera.getNumberOfCameras()方法
    PS:这个代码也只是示例代码,当然肯定不能至返回true或者false,你也应当在返回false的同时去申请用户允许调用相应的权限

    八.获取摄像机

      如果你确定APP正在运行的设备有一个摄像机,你必须请求去获取一个Camera的实例去访问它(除非你正在使用一个intent去访问camera)去访问这个私有的camera,
通过使用Camera.open()方法和保证捕捉异常,就像下面的代码所展示的:
     public static Camera getCameraInstance(){
      Camera c = null;
       try{
        c = camera.open();  // 意图去获取一个相机实例
       }catch(Exception e){
           // 相机不可获取(在使用或者不存在)
    }
     return c; // 返回空,如果相机不可获取

  警告: 总是检查异常当使用Camera.open()。检查异常失败如果相机正在使用或者不存在,将会导致你的应用被系统关闭

  在运行Android 2.3或者更高的设备上,你可以通过使用Camera.open(int)去访问具体的摄像头,上面的示例代码将访问具有多个摄像机的设备的第一个即背面摄像头。
  

   九.检查相机硬件

      一旦你获取照相机,你可以通过使用Camera.getParameters()方法获取有关其功能的进一步信息和检查返回的Camera.Parameters参数对象以获得支持的功能。当使用
API 9或者更高,使用这个Camera.getCameraInfo()去确定相机是否位于设备的正面或背面,以及图像的方向。
    PS:这里去处理相机相应的信息,如果你想做一些额外的功能,这些参数都会被用到

    十.创建一个预览类(其实这个预览类所展现的效果就是我们手机照相能看到的那个区域)

      为了让用户有效的拍摄图片或视频,他们必须能够看到设备摄像头看到的内容,一个摄像预览类是一个能够展示来自于摄像头的动态的图片数据的SurfaceView,所以
用户可规划和捕捉图片或者视频
接下来的示例代码演示怎样去创建一个能够被包含到一个View layout的基础摄像预览类,这个类实现了SurfaceViewHolder.Callback为了去捕捉回调时间为了创建和销毁
View,当需要分配相机预览输入的时候:
     public class CameraPreview extends SurfaceView implments SurfaceHolder.Callback{
         private SurfaceHolder mHolder;
         private Camera mCamera;
        
         public CameraPreview(Context context,Camera camera){
             super(context);
             mCamera = camera;
        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
             mHolder = getHolder();
             mHolder.addCallback(this);
 // deprecated setting, but required on Android versions prior to 3.0
             mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder){
// The Surface has been created, now tell the camera where to draw the preview.

     try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
}
     public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        if (mHolder.getSurface() == null){
          // preview surface does not exist
          return;
        }
        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
        }
        // set preview size and make any resize, rotate or
        // reformatting changes here


        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

  如果你想为你的预览相机设置一个具体的尺寸,设置这个在surfaceChanged()方法中如上所述,当设置预览尺寸时,你也必须使用来自 getSupportedPreviewSizes()
的值,不要在 setPreviewSize()方法里面设置随意的值
   警告:随着Android7.0和更高级别的多窗口功能的引入,你不能再假定预览的长短与调用此setDisplayOrientation()方法后的活动是相同的。根据窗口尺寸和纵横比,
你可能不得不适应相机预览为portrait-orientated 布局,或反之亦然,使用letterbox 布局
   PS:如果你不太了解这个过程。那么你得先去学一个SurfaceView的使用和相关原理,在学这个的前提是,你得懂Android原生View和ViewGroup的原理,同时你还要了解手机绘图的机制,其实电脑也是差不多的过程。都是操作系统,大同小异。就像IOS既能用在手机也能用在电脑上。Android也可以,不过Android作为手机系统的使用更广泛而已。

  十.将预览放置到布局中(就是想能让用户看到摄像机看到区域放到手机的屏幕上)

     一个相机预览类,就像之前展现的部分的例子,一定被放置在一个一个活动的布局中,伴随着其它UI控制为了照相或者摄像。这个部分是告诉你怎样去构建一个基础
的布局和一个活动为了预览
接下来的布局代码提供了一个能够被用来播放一个相机预览的非常基础的布局,在这个例子中,这个FramLayout元素意味着成为了一个相机预览类的容器,这个布局类型
是如此的实用以至于传统的照片信息或者控制器能够被覆盖在动态的相机预览上
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />
  <Button
    android:id="@+id/button_capture"
    android:text="Capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>


在大部分设备上,默认的相机方向是横向。这个例子布局具体说明了一个横向的布局并且这个这个代码接下来固定了这个APP的方向。为了简单的呈现照相机预览,你应
该改变你的APP预览Activity方向为横向通过在manifest中添加:
<activity android:name=".CameraActivity"
          android:label="@string/app_name"
          android:screenOrientation="landscape">
          <!-- configure this activity to use landscape orientation -->
          <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
   注释:一个相机预览不必称为横向模式,从Android2.2开始,你可以使用 setDisplayOrientation()方法去设置这个预览照片的横纵方向。一边在用户重新定位手机
时更改预览方向,在你的预览类的surfaceChanged()方法中,首先通过Camera.stopPreview()停止预览改变方向然后再通过Camera.startPreview()开始预览
   在你的拥有相机视图的活动中,在上面的例子中添加预览类到FramLayout元素中。你的相机活动必须也要确认当他停止或者被关闭的时候,它释放了相机。这个接下来
的示例演示了如何去修改相机活动以附加预览类在创建一个预览类中:
public class CameraActivity extends Activity {


    private Camera mCamera;
    private CameraPreview mPreview;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Create an instance of Camera
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
}
   注释:getCameraInstance()方法是在上面的访问相机(Accessing cameras)的例子中被展示了。

    十.捕捉照片

  一旦你已经建立了预览类和一个视图布局(这个视图布局去播放它),你的应用应该准备去开始捕捉照片。在你的应用代码里,你必须建立监听器为了你的UI控制去
回应用户拍照。
   为了取回一张照片,使用Camera.takePicture()方法。这个方法需要三个参数从摄像机接收数据。为了接受JPEG格式的数据,你必须实现Camera.PictureCallback接
口去接受图片数据并且写到文件里。这个接下来的代码就展示了一个基础的关于Camera.PictureCallbakc接口去保存来自于相机的照片的实现:
private PictureCallback mPicture = new PictureCallback() {


    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
        if (pictureFile == null){
            Log.d(TAG, "Error creating media file, check storage permissions: " +
                e.getMessage());
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        }
    }
};

捕捉图片通过使用Camera.takePicture()方法。接下来的例子代码展示了如何在Butoon的View.OnclickListener()调用这个方法
// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // get an image from the camera
            mCamera.takePicture(null, null, mPicture);
        }
    }
); 
   注释:这个mPicture成员是引用上面的实例代码。
   警告:记住去释放Camera类通过调用Camera.release(),当你的应用已经完成使用相机。关于怎样去释放相机的信息,看 Releasing the camera
   

  最后手撸了一张体系图,本来还想撸一张特定的代码真正该放在哪里,想了想,一张图根本画不完

    手撸的Android相机的官方文档(有自己的理解,翻译的可能有点不对)


       总结:

            花了4个多小时翻译和写demo代码。差不多算了解了。花俩个小时整理。基础的算弄懂了,如果要做进阶的东西,还是得多加一点知识储备。懂了camera是干什么的了。还有官方文档后面其实还是有一个关于摄像的,暂时不撸了,撸不动了。拾掇拾掇,自己撸一个照相类。