第三十六讲:Android手机摄像头编程入门
本讲内容:Android手机摄像头编程入门
智能手机中的摄像头和普通手机中的摄像头最大的区别在于,智能机上的摄像头可以由程序员写程序控制,做一些有趣的应用譬如,画中画,做一些有用的应用譬如二维码识别,等等。本讲打算通过一个实例,来介绍一下摄像头编程,相关解释都写在代码中了,请注意看代码注释。
实例:窈窈照相机,功能很简单,就是点击程序弹出照相预览界面,点击相机按钮完成照相功能,所照相片会存储在手机存储卡根目录。
1、创建一个项目 Lesson36_Camera ,主程序文件为 MainActivity.java
2、AndroidManifest.xml 中设置屏幕为横屏,并且声明摄像头和存储卡的使用权限,具体代码如下:
01 |
<?xml version="1.0" encoding="utf-8"?>
|
02 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="basic.android.lesson36" android:versioncode="1" android:versionname="1.0">
|
03 |
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
04 |
<activity android:name=".MainActivity" android:label="@string/app_name" android:screenorientation="landscape" android:configchanges="orientation|keyboardHidden|keyboard">
|
05 |
<intent -filter="">
|
06 |
<action android:name="android.intent.action.MAIN">
|
07 |
<category android:name="android.intent.category.LAUNCHER">
|
08 |
</category></action></intent>
|
09 |
</activity>
|
10 |
11 |
</application>
|
12 |
<uses -sdk="" android:minsdkversion="8">
|
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="">
|
17 |
</uses></uses></uses></manifest> |
3、本例中不需要布局文件main.xml,因为本例中的UI组建都是动态添加上去的。
4、最后MainActivity.java的代码如下:
001 |
package basic.android.lesson36;
|
002 |
003 |
import java.io.BufferedOutputStream;
|
004 |
import java.io.File;
|
005 |
import java.io.FileOutputStream;
|
006 |
import java.io.IOException;
|
007 |
import java.util.Calendar;
|
008 |
import java.util.Locale;
|
009 |
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;
|
027 |
028 |
public class MainActivity extends Activity {
|
029 |
030 |
private CameraView cv;
|
031 |
//准备一个相机对象
|
032 |
private Camera mCamera = null;
|
033 |
//准备一个Bitmap对象
|
034 |
private Bitmap mBitmap = null;
|
035 |
036 |
//准备一个保存图片的PictureCallback对象
|
037 |
public Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
|
038 |
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");
|
046 |
try {
|
047 |
file.createNewFile();
|
048 |
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
|
049 |
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
|
050 |
os.flush();
|
051 |
os.close();
|
052 |
Toast.makeText(getApplicationContext(), "图片保存完毕,在存储卡的根目录", Toast.LENGTH_LONG).show();
|
053 |
} catch (IOException e) {
|
054 |
e.printStackTrace();
|
055 |
}
|
056 |
}
|
057 |
058 |
};
|
059 |
060 |
//Activity的创建方法
|
061 |
@Override
|
062 |
public void onCreate(Bundle savedInstanceState) {
|
063 |
super.onCreate(savedInstanceState);
|
064 |
065 |
//窗口去掉标题
|
066 |
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
067 |
//窗口设置为全屏
|
068 |
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
069 |
//设置窗口为半透明
|
070 |
getWindow().setFormat(PixelFormat.TRANSLUCENT);
|
071 |
072 |
//提供一个帧布局
|
073 |
FrameLayout fl = new FrameLayout(this);
|
074 |
075 |
//创建一个照相预览用的SurfaceView子类,并放在帧布局的底层
|
076 |
cv = new CameraView(this);
|
077 |
fl.addView(cv);
|
078 |
079 |
//创建一个文本框添加在帧布局中,我们可以看到,文字自动出现在了SurfaceView的前面,由此你可以在预览窗口做出各种特殊效果
|
080 |
TextView tv = new TextView(this);
|
081 |
tv.setText("请按\"相机\"按钮拍摄");
|
082 |
fl.addView(tv);
|
083 |
084 |
//设置Activity的根内容视图
|
085 |
setContentView(fl);
|
086 |
087 |
}
|
088 |
089 |
//相机按键按下的事件处理方法
|
090 |
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
091 |
Log.i("yao","MainActivity.onKeyDown");
|
092 |
if (keyCode == KeyEvent.KEYCODE_CAMERA) {
|
093 |
if (mCamera != null) {
|
094 |
Log.i("yao","mCamera.takePicture");
|
095 |
//当按下相机按钮时,执行相机对象的takePicture()方法,该方法有三个回调对象做入参,不需要的时候可以设null
|
096 |
mCamera.takePicture(null, null, pictureCallback);
|
097 |
}
|
098 |
}
|
099 |
return cv.onKeyDown(keyCode, event);
|
100 |
}
|
101 |
102 |
// 照相视图
|
103 |
class CameraView extends SurfaceView {
|
104 |
105 |
private SurfaceHolder holder = null;
|
106 |
107 |
//构造函数
|
108 |
public CameraView(Context context) {
|
109 |
super(context);
|
110 |
Log.i("yao","CameraView");
|
111 |
112 |
// 操作surface的holder
|
113 |
holder = this.getHolder();
|
114 |
// 创建SurfaceHolder.Callback对象
|
115 |
holder.addCallback(new SurfaceHolder.Callback() {
|
116 |
117 |
@Override
|
118 |
public void surfaceDestroyed(SurfaceHolder holder) {
|
119 |
// 停止预览
|
120 |
mCamera.stopPreview();
|
121 |
// 释放相机资源并置空
|
122 |
mCamera.release();
|
123 |
mCamera = null;
|
124 |
}
|
125 |
126 |
@Override
|
127 |
public void surfaceCreated(SurfaceHolder holder) {
|
128 |
//当预览视图创建的时候开启相机
|
129 |
mCamera = Camera.open();
|
130 |
try {
|
131 |
//设置预览
|
132 |
mCamera.setPreviewDisplay(holder);
|
133 |
} catch (IOException e) {
|
134 |
// 释放相机资源并置空
|
135 |
mCamera.release();
|
136 |
mCamera = null;
|
137 |
}
|
138 |
139 |
}
|
140 |
141 |
//当surface视图数据发生变化时,处理预览信息
|
142 |
@Override
|
143 |
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
144 |
145 |
//获得相机参数对象
|
146 |
Camera.Parameters parameters = mCamera.getParameters();
|
147 |
//设置格式
|
148 |
parameters.setPictureFormat(PixelFormat.JPEG);
|
149 |
//设置预览大小,这里我的测试机是Milsstone所以设置的是854x480
|
150 |
parameters.setPreviewSize(854, 480);
|
151 |
//设置自动对焦
|
152 |
parameters.setFocusMode("auto");
|
153 |
//设置图片保存时的分辨率大小
|
154 |
parameters.setPictureSize(2592, 1456);
|
155 |
//给相机对象设置刚才设定的参数
|
156 |
mCamera.setParameters(parameters);
|
157 |
//开始预览
|
158 |
mCamera.startPreview();
|
159 |
}
|
160 |
});
|
161 |
// 设置Push缓冲类型,说明surface数据由其他来源提供,而不是用自己的Canvas来绘图,在这里是由摄像头来提供数据
|
162 |
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
163 |
}
|
164 |
165 |
}
|
166 |
167 |
} |
5、连接Milestone,编译并运行程序:悲剧的是,截图只能看到黑黑的一片,无法截取到摄像头传输过来SurfaceView信息,而在真机中是能看到预览效果的。
还是上一张照好的图片给大家吧,(用了好多年的小黑……)
好了,本讲就到这里,谢谢大家的支持和鼓励,下次再见。