本讲内容:Android手机摄像头编程入门
智能手机中的摄像头和普通手机中的摄像头最大的区别在于,智能机上的摄像头可以由程序员写程序控制,做一些有趣的应用譬如,画中画,做一些有用的应用譬如二维码识别,等等。本讲打算通过一个实例,来介绍一下摄像头编程,相关解释都写在代码中了,请注意看代码注释。
实例:窈窈照相机,功能很简单,就是点击程序弹出照相预览界面,点击相机按钮完成照相功能,所照相片会存储在手机存储卡根目录。1、创建一个项目 Lesson36_Camera ,主程序文件为 MainActivity.java
2、AndroidManifest.xml 中设置屏幕为横屏,并且声明摄像头和存储卡的使用权限,具体代码如下:
01 | <?xml version= "1.0"
encoding= "utf-8" ?> |
03 | <APPLICATION android:icon= "@drawable/icon"
android:label= "@string/app_name" > |
04 | <ACTIVITY android:label= "@string/app_name"
android:configchanges= "orientation|keyboardHidden|keyboard"
android:screenorientation= "landscape"
android:name= ".MainActivity" > |
06 | <ACTION android:name= "android.intent.action.MAIN"
/> |
07 | <CATEGORY android:name= "android.intent.category.LAUNCHER"
/> |
12 | <USES android:minsdkversion= "8"
-sdk /> |
13 | <USES android:name= "android.permission.CAMERA"
-permission></USES> |
14 | <USES android:name= "android.permission.WRITE_EXTERNAL_STORAGE"
-permission></USES> |
15 | <USES android:name= "android.hardware.camera"
-feature /> |
16 | <USES android:name= "android.hardware.camera.autofocus"
-feature /> |
3、本例中不需要布局文件main.xml,因为本例中的UI组建都是动态添加上去的。
4、最后MainActivity.java的代码如下:
001 | package
basic.android.lesson36; |
003 | import
java.io.BufferedOutputStream; |
005 | import
java.io.FileOutputStream; |
006 | import
java.io.IOException; |
007 | import
java.util.Calendar; |
008 | import
java.util.Locale; |
010 | import
android.app.Activity; |
011 | import
android.content.Context; |
012 | import
android.graphics.Bitmap; |
013 | import
android.graphics.BitmapFactory; |
014 | import
android.graphics.PixelFormat; |
015 | import
android.hardware.Camera; |
016 | import
android.os.Bundle; |
017 | import
android.text.format.DateFormat; |
018 | import
android.util.Log; |
019 | import
android.view.KeyEvent; |
020 | import
android.view.SurfaceHolder; |
021 | import
android.view.SurfaceView; |
022 | import
android.view.Window; |
023 | import
android.view.WindowManager; |
024 | import
android.widget.FrameLayout; |
025 | import
android.widget.TextView; |
026 | import
android.widget.Toast; |
028 | public
class
MainActivity extends
Activity { |
032 | private
Camera mCamera = null ; |
034 | private
Bitmap mBitmap = null ; |
036 | //准备一个保存图片的PictureCallback对象 |
037 | public
Camera.PictureCallback pictureCallback = new
Camera.PictureCallback() { |
039 | public
void
onPictureTaken( byte [] data, Camera camera) { |
040 | Log.i( "yao" , "onPictureTaken" ); |
041 | Toast.makeText(getApplicationContext(), "正在保存……" , Toast.LENGTH_LONG).show(); |
042 | //用BitmapFactory.decodeByteArray()方法可以把相机传回的裸数据转换成Bitmap对象 |
043 | mBitmap = BitmapFactory.decodeByteArray(data, 0 , data.length); |
044 | //接下来的工作就是把Bitmap保存成一个存储卡中的文件 |
045 | File file = new
File( "/sdcard/YY" + new
DateFormat().format( "yyyyMMdd_hhmmss" , Calendar.getInstance(Locale.CHINA)) + ".jpg" ); |
047 | file.createNewFile(); |
048 | BufferedOutputStream os = new
BufferedOutputStream( new
FileOutputStream(file)); |
049 | mBitmap.compress(Bitmap.CompressFormat.PNG, 100 , os); |
052 | Toast.makeText(getApplicationContext(), "图片保存完毕,在存储卡的根目录" , Toast.LENGTH_LONG).show(); |
053 | } catch
(IOException e) { |
062 | public
void
onCreate(Bundle savedInstanceState) { |
063 | super .onCreate(savedInstanceState); |
066 | requestWindowFeature(Window.FEATURE_NO_TITLE); |
068 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); |
070 | getWindow().setFormat(PixelFormat.TRANSLUCENT); |
073 | FrameLayout fl = new
FrameLayout( this ); |
075 | //创建一个照相预览用的SurfaceView子类,并放在帧布局的底层 |
076 | cv = new
CameraView( this ); |
079 | //创建一个文本框添加在帧布局中,我们可以看到,文字自动出现在了SurfaceView的前面,由此你可以在预览窗口做出各种特殊效果 |
080 | TextView tv = new
TextView( this ); |
081 | tv.setText( "请按\"相机\"按钮拍摄" ); |
090 | public
boolean
onKeyDown( int
keyCode, KeyEvent event) { |
091 | Log.i( "yao" , "MainActivity.onKeyDown" ); |
092 | if
(keyCode == KeyEvent.KEYCODE_CAMERA) { |
094 | Log.i( "yao" , "mCamera.takePicture" ); |
095 | //当按下相机按钮时,执行相机对象的takePicture()方法,该方法有三个回调对象做入参,不需要的时候可以设null |
096 | mCamera.takePicture( null , null , pictureCallback); |
099 | return
cv.onKeyDown(keyCode, event); |
103 | class
CameraView extends
SurfaceView { |
105 | private
SurfaceHolder holder = null ; |
108 | public
CameraView(Context context) { |
110 | Log.i( "yao" , "CameraView" ); |
113 | holder = this .getHolder(); |
114 | // 创建SurfaceHolder.Callback对象 |
115 | holder.addCallback( new
SurfaceHolder.Callback() { |
118 | public
void
surfaceDestroyed(SurfaceHolder holder) { |
120 | mCamera.stopPreview(); |
127 | public
void
surfaceCreated(SurfaceHolder holder) { |
129 | mCamera = Camera.open(); |
132 | mCamera.setPreviewDisplay(holder); |
133 | } catch
(IOException e) { |
141 | //当surface视图数据发生变化时,处理预览信息 |
143 | public
void
surfaceChanged(SurfaceHolder holder, int
format, int
width, int
height) { |
146 | Camera.Parameters parameters = mCamera.getParameters(); |
148 | parameters.setPictureFormat(PixelFormat.JPEG); |
149 | //设置预览大小,这里我的测试机是Milsstone所以设置的是854x480 |
150 | parameters.setPreviewSize( 854 , 480 ); |
152 | parameters.setFocusMode( "auto" ); |
154 | parameters.setPictureSize( 2592 , 1456 ); |
156 | mCamera.setParameters(parameters); |
158 | mCamera.startPreview(); |
161 | // 设置Push缓冲类型,说明surface数据由其他来源提供,而不是用自己的Canvas来绘图,在这里是由摄像头来提供数据 |
162 | holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); |
5、连接Milestone,编译并运行程序:悲剧的是,截图只能看到黑黑的一片,无法截取到摄像头传输过来SurfaceView信息,而在真机中是能看到预览效果的。
还是上一张照好的图片给大家吧,