一、新建Andriod应用项目
生成blank activity
二、配置AndroidManifest.xml
摄像权限<uses-permission android:name="android.permission.CAMERA" />
写SDK卡权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
三、修改activity_main.xml如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</RelativeLayout>
四、实现CameraView类,继承SurfaceView并实现SurfaceHolder.Callback、Camera.PictureCallback接口
1、SurfaceView的介绍(来自网络)
SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
Surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。Surfaceview提供了一个可见区域,只有在这个可见区域内 的Surface部分内容才可见,可见区域
外的部分不可见。Surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 Surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置
遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果Surface上面 有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。
你可以通过SurfaceHolder接口访问这个Surface,getHolder()方法可以得到这个接口。Surfaceview变得可见时,Surface被创建;Surfaceview隐藏前,Surface被销毁。
这样能节省资源。如果你要查看 Surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
Surfaceview的核心在于提供了两个线程:UI线程和渲染线程。这里应注意:
a.所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。
b.由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的Surface。
2、Camera.PictureCallback接口
通过实现onPictureTaken方法来得到拍照后的图片的二进制数据
详见http://android.toolib.net/reference/android/hardware/Camera.PictureCallback.html
3、Handler类和Runnable类
Handler是来满足线程间的通信,Handler先进先出的原则,可以对运行在不同线程中的多个任务进行排队,Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联。每个Handler的实例都关联了一个线程和线程的消息队列。当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将发送和处理这些消息或Runnable对象。
Runnable是接口类,实现方法run来执行要做的事情。使用Handler的post方法将Runnable对象放到Handler的线程队列中后,该Runnable的执行其实并未单独开启线程,而是仍然在当前Activity线程中执行的,Handler只是调用了Runnable对象的run方法。
4、Camera类,几个主要函数
camera = Camera.open();// 摄像头的初始化
camera.setPreviewDisplay(holder)//告诉camera在哪画预览
camera.setParameters(parameters);// 设置参数
camera.startPreview();// 开始预览
camera.takePicture//对焦后拍照
camera.setPreviewDisplay(null);//摄像头用完调三个函数
camera.stopPreview();
camera.release();
5、代码
public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
Camera.PictureCallback {
private SurfaceHolder holder;
private Camera camera;
// private Camera.Parameters parameters;
private Activity act;
private Handler handler = new Handler();
@SuppressWarnings("deprecation")
public CameraView(Context context, Activity act) {
super(context);
this.act = act;
if (!checkCameraHardware(context)) {
return;
}
holder = getHolder();// 生成Surface Holder
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 指定Push Buffer
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (camera == null) {
handler.postDelayed(this, 1 * 1000);// 由于启动camera需要时间,在此让其等两秒再进行聚焦知道camera不为空
} else {
camera.autoFocus(new AutoFocusCallback() {
// @SuppressLint("NewApi")
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
// camera.enableShutterSound(false);
camera.takePicture(new ShutterCallback() {// 如果聚焦成功则进行拍照
@Override
public void onShutter() {
}
}, null, CameraView.this);
} else {
}
}
});
}
}
}, 2 * 1000);//2 s
}
@Override
public void surfaceCreated(final SurfaceHolder holder) {
// TODO Auto-generated method stub
try {
camera = Camera.open();// 摄像头的初始化
} catch (Exception e) {
// Camera is not available (in use or does not exist)
}
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (holder != null) {
try { // The Surface has been created, now tell the camera
// where to draw the preview
camera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
handler.postDelayed(this, 1 * 1000);
}
}
}, 2 * 1000);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
// parameters = camera.getParameters();
// camera.setParameters(parameters);// 设置参数
camera.startPreview();// 开始预览
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
void ReleaseCamera() {
if (camera != null) {
try {
camera.setPreviewDisplay(null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
camera.stopPreview();
camera.release();
camera = null;
}
}
@SuppressLint("SimpleDateFormat")
public void onPictureTaken(byte[] data, Camera camera) {// 拍摄完成后保存照片
try {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String time = format.format(date);
// 在SD卡上创建文件夹
File file = new File(Environment.getExternalStorageDirectory()
+ "/myCamera/pic");
if (!file.exists()) {
file.mkdirs();
}
String path = Environment.getExternalStorageDirectory()
+ "/myCamera/pic/" + time + ".jpg";
data2file(data, path);
ReleaseCamera();
holder.removeCallback(CameraView.this);
act.finish();
} catch (Exception e) {
}
}
private void data2file(byte[] w, String fileName) throws Exception {// 将二进制数据转换为文件的函数
FileOutputStream out = null;
try {
out = new FileOutputStream(fileName);
out.write(w);
out.close();
Toast.makeText(act, "Image saved to:\n" + fileName,
Toast.LENGTH_LONG).show();
} catch (Exception e) {
if (out != null)
out.close();
throw e;
}
}
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
// this device has a camera
return true;
} else {
// no camera on this device
Toast.makeText(act, "No camera!!!\n", Toast.LENGTH_LONG).show();
return false;
}
}
}
五、修改MainActivity.onCreate函数
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);// 设置横屏模式以及全屏模式
view = new CameraView(this,this);// 通过一个surfaceview的view来实现拍照
view.setId(1);
setContentView(R.layout.activity_main);
RelativeLayout relative = (RelativeLayout) this.findViewById(R.id.ly);
RelativeLayout.LayoutParams Layout = new RelativeLayout.LayoutParams(3,3);// 设置surfaceview使其满足需求无法观看预览
Layout.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 1);
Layout.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 1);
relative.addView(view, Layout);
}
六、其他
1、如何关掉拍照声音,有个函数camera.enableShutterSound,但要API 17才能用。网上找了下,有说c改属性值可以实现,后面再研究。
2、更多可参考http://developer.android.com/guide/topics/media/camera.html