我们需要使用到的是两个比较重要的类:Camera(注意这里的Camera类是在android.hardware.Camera这个包下的)和Camera.Parameters 这两个类的对象分别对应于一个摄像头硬件和摄像头参数集。同时我们还需要使用到两个比较重要的接口:SurfaceHolder.Callback和PictureCallback 这两个类可以实现预览画面和拍照的方法。
首先我们先创建一个简单的布局文件
<strong><span style="font-size:18px;"><RelativeLayout 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"
tools:context=".CameraActivity">
<SurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/camera_surfaceView"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:id="@+id/camera_btn_change"
android:text="切换摄像头"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:id="@+id/camera_btn_photo"
android:text="拍照"/>
</RelativeLayout></span></strong>
同时我们也要像系统申请摄像头的权限和操作SD卡的权限:
<strong><span style="font-family:FangSong_GB2312;font-size:18px;"><uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission></span></strong>
并且我们需要让Activity固定竖屏(实际需要看个人,一般来说固定Activity的方向比较好):
<activity
android:name=".CameraActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
接下来是我们的JAVA代码我们需要实现SurfaceHolder.Callback和PictureCallback两个类的接口,同时实现对应的几个方法:
SurfcaceHolder.Callback的三个需要实现的方法:
surfacCreated方法会在SurfaceView显示的时候系统回调
<span style="font-family:FangSong_GB2312;font-size:18px;"><strong>@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
//将摄像头取景给到一个SurfaceHolder对象 SurfaceHolder对象会在Surface显示取景
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
//这里是旋转摄像头,因为摄像头都是横置(有些机型也可能是竖立的,根据情况来调整)
camera.setDisplayOrientation(90);
//设置摄像头参数
camera.setParameters(parameters);
//开始显示预览
camera.startPreview();
}</strong></span>
surfacChanged方法会在SurfaceView形状改变的时候调用
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
if (surfaceHolder.getSurface()==null){
return;
}
//先停止预览
camera.stopPreview();
//将取景设置到SurfaceHolder对象
try {
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
//旋转摄像头
camera.setDisplayOrientation(90);
//设置摄像头参数
camera.setParameters(parameters);
//开始预览
camera.startPreview();
}
surfaceDestroyed方法会在surfView消失的时候调用
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (camera!=null){
camera.stopPreview();
//如果进入这个Activity始终是从onCreat方法进入的则可以释放
//如果是从onRestart进入的则不能释放,因为在surfaceCreated方法里面并没有实例化Camera对象
//camera.release();
}
}
PictureCallback需要实现的方法,这个方法会在我们执行Camera的takePicture()方法的时候,系统来调用。我们可以在这里获取到摄像头拍下的相片数据
我们可以对数据进行一些简单的处理,这里主要就是保存相片
@Override
public void onPictureTaken(byte[] bytes, Camera camera) {
//通过BitmapFactory的decodeByteArray()方法将获取到的相片数据给一个Bitmap对象赋值
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes,0,bytes.length);
//创建一个Matrix对象 用于旋转照片 因为初始的照片的方向是和人观察的相反的
Matrix matrix = new Matrix();
//如果是后置摄像头 则顺时针旋转90度 反之逆时针旋转90度
if (cameraCount%cameraId==0){
matrix.setRotate(90);
}else if (cameraCount%cameraId==1){
matrix.setRotate(-90);
}
//通过Bitmap的createBitmap方法来旋转图片
bitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
//定义一个输出缓存流
BufferedOutputStream bos = null;
//在SD卡创建一个存放照片的目录
File file = new File("mnt/sdcard/mycamera/picture/");
file.mkdirs();
//为照片取一个默认的名字
String fileName = DateFormat.format("yyyy-MM-dd-hh:mm:ss", Calendar.getInstance())+".jpg";
//照片的绝对路径
String path = "mnt/sdcard/mycamera/picture"+fileName;
try {
//为照片创建一个缓存输出流
bos = new BufferedOutputStream(new FileOutputStream(path));
//将bitmap的数据写入bos里
bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
现在我们需要在Acitvity的onCreate方法里面执行一些初始化的操作:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//取消标题栏和设置全屏
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//设置屏幕高亮 这个很重要 不然无法正常显示预览
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.layout_camera);
btn_photo = (Button) findViewById(R.id.camera_btn_photo);
btn_change = (Button) findViewById(R.id.camera_btn_change);
cameraView = (SurfaceView) findViewById(R.id.camera_surfaceView);
//打开默认摄像头
camera = Camera.open();
//获取摄像头初始参数集
parameters = camera.getParameters();
//获取摄像头个数
cameraId = Camera.getNumberOfCameras();
//为按钮设置监听
btn_photo.setOnClickListener(this);
btn_change.setOnClickListener(this);
//通过SurfaceView对象获取一个SurfaceHolder对象 并且用SurfaceHolder对象来调用addCallback方法
cameraView.getHolder().addCallback(this);
}
启动拍照的方法
camera.takePicture(null,null,this);
完整代码:
package wpf.app.mycamera;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.text.format.DateFormat;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
public class CameraActivity extends ActionBarActivity implements View.OnClickListener, SurfaceHolder.Callback, Camera.PictureCallback {
SurfaceView cameraView;
Button btn_photo;
Button btn_change;
int cameraCount;
int cameraId;
Camera camera;
Camera.Parameters parameters;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//取消标题栏和设置全屏
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//设置屏幕高亮 这个很重要 不然无法正常显示预览
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.layout_camera);
btn_photo = (Button) findViewById(R.id.camera_btn_photo);
btn_change = (Button) findViewById(R.id.camera_btn_change);
cameraView = (SurfaceView) findViewById(R.id.camera_surfaceView);
//打开默认摄像头
camera = Camera.open();
//获取摄像头初始参数集
parameters = camera.getParameters();
//获取摄像头个数
cameraId = Camera.getNumberOfCameras();
//为按钮设置监听
btn_photo.setOnClickListener(this);
btn_change.setOnClickListener(this);
//通过SurfaceView对象获取一个SurfaceHolder对象 并且用SurfaceHolder对象来调用addCallback方法
cameraView.getHolder().addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
//将摄像头取景给到一个SurfaceHolder对象 SurfaceHolder对象会在Surface显示取景
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
//这里是旋转摄像头,因为摄像头都是横置(有些机型也可能是竖立的,根据情况来调整)
camera.setDisplayOrientation(90);
//设置摄像头参数
camera.setParameters(parameters);
//开始显示预览
camera.startPreview();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
if (surfaceHolder.getSurface() == null) {
return;
}
//先停止预览
camera.stopPreview();
//将取景设置到SurfaceHolder对象
try {
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
//旋转摄像头
camera.setDisplayOrientation(90);
//设置摄像头参数
camera.setParameters(parameters);
//开始预览
camera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (camera != null) {
camera.stopPreview();
//如果进入这个Activity始终是从onCreat方法进入的则可以释放
//如果是从onRestart进入的则不能释放,因为在surfaceCreated方法里面并没有实例化Camera对象
//camera.release();
}
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.camera_btn_change:
if (camera!=null){
camera.stopPreview();
camera.release();
}
cameraCount++;
camera = Camera.open(cameraCount%cameraId);
try {
camera.setPreviewDisplay(cameraView.getHolder());
camera.setDisplayOrientation(90);
camera.setParameters(parameters);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
break;
case R.id.camera_btn_photo:
//自动对焦
camera.autoFocus(null);
camera.takePicture(null,null,this);
}
}
@Override
public void onPictureTaken(byte[] bytes, Camera camera) {
//通过BitmapFactory的decodeByteArray()方法将获取到的相片数据给一个Bitmap对象赋值
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
//创建一个Matrix对象 用于旋转照片 因为初始的照片的方向是和人观察的相反的
Matrix matrix = new Matrix();
//如果是后置摄像头 则顺时针旋转90度 反之逆时针旋转90度
if (cameraCount % cameraId == 0) {
matrix.setRotate(90);
} else if (cameraCount % cameraId == 1) {
matrix.setRotate(-90);
}
//通过Bitmap的createBitmap方法来旋转图片
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
//定义一个输出缓存流
BufferedOutputStream bos = null;
//在SD卡创建一个存放照片的目录
File file = new File("mnt/sdcard/mycamera/picture/");
file.mkdirs();
//为照片取一个默认的名字
String fileName = DateFormat.format("yyyy-MM-dd-hh:mm:ss", Calendar.getInstance()) + ".jpg";
//照片的绝对路径
String path = "mnt/sdcard/mycamera/picture" + fileName;
try {
//为照片创建一个缓存输出流
bos = new BufferedOutputStream(new FileOutputStream(path));
//将bitmap的数据写入bos里
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}