Android之Camera

来源:互联网 发布:淘宝反作弊部门情况 编辑:程序博客网 时间:2024/06/14 04:36

今天无意当中发现在《Android开发学习之基于ZBar实现微信扫一扫》中的一部分代码可以用来以硬件方式实现一个照相机的功能,在《Android开发学习之调用系统相机完成拍照的实现》中我们是以Intent方式调用系统内置的相机来完成拍照的,今天呢,就让我们来以Camera类为核心来开发自己的相机应用吧。在Android的官方文档中,以硬件方式定制相机应用的步骤如下:


1. 检查和访问Camera

创建代码来检查Camera和所申请访问的存在性;

2. 创建一个预览类

继承SurfaceView来创建一个Camera的预览类,并实现SurfaceHolder接口。这个类用来预览来自Camera的实施图像。

3. 构建一个预览布局

一旦有了Camera预览类,就可以把这个预览类和你想要的用户界面控制结合在一起来创建一个视图布局。

4. 针对采集建立监听

把监听器与响应用户动作(如按下按钮)的界面控制连接到一起来启动图像或视频的采集。

5. 采集和保存文件

针对真正采集的图片或视频,以及输出的保存来编写代码。

6. 释放Camera

使用Camera之后,你的应用程序必须释放Camera,以便其他应用程序能够使用。

Camera硬件是一个必须要认真管理的共享资源,因此你的应用程序在使用它时,不能跟其他应用程序发生冲突。下文将讨论如何检查Camera硬件、如何申请对Camera的访问,如何采集图片和视频,以及在应用使用完成后如何释放Camera。

警告:在应用程序使用完Camera时,要记住通过调用Camera.release()方法来释放Camera对象。如果你的应用程序没有正确的释放Camera,所有的后续的视图对Camera的访问,包括你自己的应用程序,都会失败,并可能到你的或其他的应用程序关闭。

下面我们就来按照上面的步骤来一步步的制作一个属于自己的相机应用吧!

第一步:检查和访问相机

?
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
/** 官方建议的安全地访问摄像头的方法  **/
    publicstatic Camera getCameraInstance(){
        Camera c = null;
        try{
            c = Camera.open();
        }
        catch(Exception e){
        Log.d("TAG","Error is "+e.getMessage());
        }
        returnc;
    }
     
    /** 检查设备是否支持摄像头  **/
    privateboolean CheckCameraHardware(Context mContext)
    {
        if(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA))
        {
            // 摄像头存在
            returntrue;
        }else{
            // 摄像头不存在
            returnfalse;
        }
    }
    

第二步:创建一个预览类。这里我们使用的是官方API中提供的一个基本的预览类:

?
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
packagecom.android.OpenCamera;
 
importjava.io.IOException;
 
importandroid.annotation.SuppressLint;
importandroid.content.Context;
importandroid.hardware.Camera;
importandroid.util.Log;
importandroid.view.SurfaceHolder;
importandroid.view.SurfaceView;
 
/** 一个基本的相机预览界面类    **/
@SuppressLint("ViewConstructor")
publicclass CameraPreview extendsSurfaceView implementsSurfaceHolder.Callback {
 
    /** Camera **/
    privateCamera mCamera;
    /** SurfaceHolder **/
    privateSurfaceHolder mHolder;
     
    /** CamreaPreview构造函数   **/
    @SuppressWarnings("deprecation")
    publicCameraPreview(Context mContext,Camera mCamera)
    {
        super(mContext);
        this.mCamera=mCamera;
        // 安装一个SurfaceHolder.Callback,
        // 这样创建和销毁底层surface时能够获得通知。
        mHolder = getHolder();
        mHolder.addCallback(this);
        // 已过期的设置,但版本低于3.0的Android还需要
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
 
    @Override
    publicvoid surfaceChanged(SurfaceHolder arg0, intarg1, intarg2, intarg3)
    {
         
    }
 
    @Override
    publicvoid surfaceCreated(SurfaceHolder holder)
    {
       try{
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();
        mCamera.setDisplayOrientation(90);
       }catch(IOException e) {
        Log.d("TAG","Error is "+e.getMessage());
       }   
    }
 
    @Override
    publicvoid surfaceDestroyed(SurfaceHolder arg0)
    {
        // 如果预览无法更改或旋转,注意此处的事件
        // 确保在缩放或重排时停止预览
        if(mHolder.getSurface() == null){
          // 预览surface不存在
          return;
        }
        // 更改时停止预览
        try{
            mCamera.stopPreview();
        }catch(Exception e){
          // 忽略:试图停止不存在的预览
        }
        // 在此进行缩放、旋转和重新组织格式
        // 以新的设置启动预
        try{
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setDisplayOrientation(90);
            mCamera.startPreview();
        }catch(Exception e){
            Log.d("TAG","Error is " + e.getMessage());
        }
         
    }
 
}

第三步:构建预览布局。这里我们使用FrameLayout来加载第二步创建的预览类,使用一个按钮进行拍照并完成储存,使用一个ImageView显示拍照的缩略图,当我们点击这个缩略图时时,系统将会调用相应的程序来打开这个图片。

?
1
2
3
4
5
6
7
8
9
10
11
12
<linearlayout 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:orientation="vertical"tools:context=".MainActivity">
    <frameLayout
        android:id="@+id/PreviewView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.8">
    </frameLayout>
    <relativelayout android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="0.2"android:background="@drawable/main_bg">
        <button android:id="@+id/BtnCapture"android:layout_width="60dp"android:layout_height="60dp"android:layout_centerhorizontal="true"android:layout_centervertical="true"android:background="@drawable/camera">
        <imageview android:id="@+id/ThumbsView"android:layout_width="60dp"android:layout_height="60dp"android:layout_alignparentright="true"android:layout_centervertical="true"android:layout_margin="15dp"android:contentdescription="@string/Description">
     
</imageview></button></relativelayout></linearlayout>

第四步:针对采集监听。其中PictureCallback接口用于处理拍照的结果,这里我们做三件事情,第一,保存图片;第二,显示缩略图;第三、保存当前图片的Uri,以便于系统的访问。ShutterCallback接口用于处理按下快门的事件,这里我们使用MediaPlayer类来播放快门按下时的音效。

?
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
/** 拍照回调接口  **/
    privatePictureCallback mPicureCallback=newPictureCallback()
    {
        @Override
        publicvoid onPictureTaken(byte[] mData, Camera camera)
        {
            File mPictureFile = StorageHelper.getOutputFile(StorageHelper.MEDIA_TYPE_IMAGE);
            if(mPictureFile == null){ 
                return;
            }
            try{
                /** 存储照片  **/
                FileOutputStream fos = newFileOutputStream(mPictureFile);
                fos.write(mData);
                fos.close();
                /** 设置缩略图  **/
                Bitmap mBitmap=BitmapFactory.decodeByteArray(mData, 0, mData.length);
                ThumbsView.setImageBitmap(mBitmap);
                /** 获取缩略图Uri  **/
                mUri=StorageHelper.getOutputUri(mPictureFile);
                /**停止预览**/
                mCamera.stopPreview();
                /**开始预览**/
                mCamera.startPreview();
            }catch(FileNotFoundException e) {
                e.printStackTrace();
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    };
    
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/** 快门回调接口  **/
    privateShutterCallback mShutterCallback=newShutterCallback()
    {
        @Override
        publicvoid onShutter()
        {
            mPlayer=newMediaPlayer();
            mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter);
            try{
                mPlayer.prepare();
            }catch(IllegalStateException e) {
                e.printStackTrace();
            }catch(IOException e) {
                e.printStackTrace();
            }
            mPlayer.start();
        }
    };

第五步:采集与保存。采集与保存在第四步已经给出了,这里给出一个用于保存文件的辅助类StorageHelper类:

?
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
packagecom.android.OpenCamera;
 
importjava.io.File;
importjava.text.SimpleDateFormat;
importjava.util.Date;
 
importandroid.annotation.SuppressLint;
importandroid.net.Uri;
importandroid.os.Environment;
 
@SuppressLint("SimpleDateFormat")
publicfinal class StorageHelper
{
   publicstatic final int MEDIA_TYPE_IMAGE=0;
   publicstatic final int MEDIA_TYPE_VIDEO=1;
    
   publicstatic Uri  getOutputUri(File mFile)
   {
       returnUri.fromFile(mFile);
   }
    
    
   publicstatic File getOutputFile(intmType)
   {
       File mMediaFileDir=newFile(Environment.getExternalStorageDirectory(),"OpenCamera");
       if(!mMediaFileDir.exists())
       {
           if(!mMediaFileDir.mkdir())
            {
               returnnull;
            }
       }
       File mMediaFile=null;
       /**  创建文件名   **/
       String mFileName=newSimpleDateFormat("yyyyMMddHHmmss").format(newDate());
       switch(mType)
       {
         caseMEDIA_TYPE_IMAGE:
             mMediaFile=newFile(mMediaFileDir.getPath()+File.separator+"IMG_"+mFileName+".jpg");
             break;
         caseMEDIA_TYPE_VIDEO:
             mMediaFile=newFile(mMediaFileDir.getPath()+File.separator+"VID_"+mFileName+".mp4");
             break;
             default:
                 mMediaFile=null;
       }
       returnmMediaFile;
   }
}

第六步:相机的释放

?
1
2
3
4
if(mCamera != null){
            mCamera.release();
            mCamera = null;
        }

通过以上步骤,我们就完成了一个简单的相机,最后给出主要的逻辑代码:

?
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
packagecom.android.OpenCamera;
 
 
importjava.io.File;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
 
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.hardware.Camera;
importandroid.hardware.Camera.AutoFocusCallback;
importandroid.hardware.Camera.PictureCallback;
importandroid.hardware.Camera.ShutterCallback;
importandroid.media.MediaPlayer;
importandroid.net.Uri;
importandroid.os.Bundle;
importandroid.app.Activity;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.pm.PackageManager;
importandroid.util.Log;
importandroid.view.Menu;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.view.Window;
importandroid.view.WindowManager;
importandroid.widget.Button;
importandroid.widget.FrameLayout;
importandroid.widget.ImageView;
importandroid.widget.Toast;
 
publicclass MainActivity extendsActivity {
 
     
    /** 相机   **/
    privateCamera mCamera; 
    /** 预览界面  **/
    privateCameraPreview mPreview;
    /** 缩略图  **/
    ImageView ThumbsView;
    /** 当前缩略图Uri **/
    privateUri mUri;
    /** MediaPlayer **/
    privateMediaPlayer mPlayer;
    @Override
    protectedvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /** 隐藏标题栏  **/
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        /** 隐藏状态栏  **/
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        /** 禁用锁屏,因为再次唤醒屏幕程序就会终止,暂时不知道怎么解决  **/
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.activity_main);
         
        /** 硬件检查  **/
        if(CheckCameraHardware(this)==false)
        {
            Toast.makeText(this,"很抱歉,您的设备可能不支持摄像头功能!", Toast.LENGTH_SHORT).show();
            return;
        }
         
        /** 获取相机  **/
        mCamera=getCameraInstance();
        /** 获取预览界面   **/
        mPreview = newCameraPreview(this, mCamera);
        FrameLayout mFrameLayout = (FrameLayout)findViewById(R.id.PreviewView);
        mFrameLayout.addView(mPreview);
        mCamera.startPreview();
        /** 拍照按钮  **/
        Button BtnCapture = (Button) findViewById(R.id.BtnCapture);
        BtnCapture.setOnClickListener(newOnClickListener()
        {
            @Override
            publicvoid onClick(View v)
            {
                /** 使用takePicture()方法完成拍照  **/
                mCamera.autoFocus(newAutoFocusCallback()
                {
                    /** 自动聚焦聚焦后完成拍照  **/
                    @Override
                    publicvoid onAutoFocus(booleanisSuccess, Camera camera)
                    {
                        if(isSuccess&&camera!=null)
                        {
                            mCamera.takePicture(mShutterCallback,null, mPicureCallback);
                        }
                    }
                     
                });
            }
        });
         
        /** 相机缩略图  **/
         
        ThumbsView = (ImageView)findViewById(R.id.ThumbsView);
        ThumbsView.setOnClickListener(newOnClickListener()
        {
            @Override
            publicvoid onClick(View v)
            {
                /** 使用Uri访问当前缩略图  **/
                Intent intent = newIntent(Intent.ACTION_VIEW);
                intent.setDataAndType(mUri,"image/*");
                startActivity(intent);
            }
        });
    }
    /** 快门回调接口  **/
    privateShutterCallback mShutterCallback=newShutterCallback()
    {
        @Override
        publicvoid onShutter()
        {
            mPlayer=newMediaPlayer();
            mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter);
            try{
                mPlayer.prepare();
            }catch(IllegalStateException e) {
                e.printStackTrace();
            }catch(IOException e) {
                e.printStackTrace();
            }
            mPlayer.start();
        }
    };
     
    /** 拍照回调接口  **/
    privatePictureCallback mPicureCallback=newPictureCallback()
    {
        @Override
        publicvoid onPictureTaken(byte[] mData, Camera camera)
        {
            File mPictureFile = StorageHelper.getOutputFile(StorageHelper.MEDIA_TYPE_IMAGE);
            if(mPictureFile == null){ 
                return;
            }
            try{
                /** 存储照片  **/
                FileOutputStream fos = newFileOutputStream(mPictureFile);
                fos.write(mData);
                fos.close();
                /** 设置缩略图  **/
                Bitmap mBitmap=BitmapFactory.decodeByteArray(mData, 0, mData.length);
                ThumbsView.setImageBitmap(mBitmap);
                /** 获取缩略图Uri  **/
                mUri=StorageHelper.getOutputUri(mPictureFile);
                /**停止预览**/
                mCamera.stopPreview();
                /**开始预览**/
                mCamera.startPreview();
            }catch(FileNotFoundException e) {
                e.printStackTrace();
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    };
     
     
    /** 官方建议的安全地访问摄像头的方法  **/
    publicstatic Camera getCameraInstance(){
        Camera c = null;
        try{
            c = Camera.open();
        }
        catch(Exception e){
        Log.d("TAG","Error is "+e.getMessage());
        }
        returnc;
    }
     
    /** 检查设备是否支持摄像头  **/
    privateboolean CheckCameraHardware(Context mContext)
    {
        if(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA))
        {
            // 摄像头存在
            returntrue;
        }else{
            // 摄像头不存在
            returnfalse;
        }
    }
     
    @Override
    protectedvoid onPause() {
        super.onPause();
        if(mCamera != null){
            mCamera.release();
            mCamera = null;
        }
    }
 
    @Override
    protectedvoid onResume()
    {
        super.onResume();
        if(mCamera == null)
        {
            mCamera = getCameraInstance();
            mCamera.startPreview();    
        }
    }
 
    @Override
    publicboolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        returntrue;
    }
 
}

这里一直有一个困惑我的问题,就是当手机锁屏以后再次唤醒屏幕,程序会立即终止。因为找不到原因,所以目前只能用代码控制设备禁止锁屏来避免这个问题的发生。这个问题和《Android开发学习之基于ZBar实现微信扫一扫》是一样的,留给大家去思考了。记得给程序加入以下权限:

?
1
2
3
<uses-permission android:name="android.permission.CAMERA">
        <uses-feature android:name="android.hardware.camera">
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission></uses-feature></uses-permission>

晚上看到Camera360开放了SDK的消息。主推拍照和滤镜API,其实开源的滤镜有很多啦,结合我们今天的例子,我们完全可以做出一个不错的相机应用来,作为长期受益于开源社区的回报,我决定在整合滤镜以后把全部的程序开源,希望大家支持我哦。唉,明天要考试了,我竟然还能如此充满激情的编程,其实这就是编程的魅力啦,在编程的世界里,我可以完全按照自己的意愿去做自己喜欢的,而这就够了,当老师和同学们都觉得我在抱怨专业课的时候,其实我只是想让自己的内心感到快乐而已。好了,晚安,各位!

(全文转载自http://www.2cto.com/kf/201401/272099.htmlAndroid开发学习之以Camera方式实现相机功能(一)——快速实现相机)


0 0