Camera API 和 SurfaceView介绍
Camera API 是Android提供的用于拍照的类,Camera实例提供了对设备相机硬件级别的调用。相机是一种独占性资源,一次只能有一个Activity调用相机。
也就是说,在使用Camera时,需要时使用,用完需立即释放,若忘记释放,除非重启设备,否则其他应用将无法使用相机。
管理Camera实例有如下几种方法:
public static Camera open(int cameraId)
//API 8及以下版本初始化Camera实例,默认打开后置摄像头
public static Camera open()
//Activity/Fragment被销毁时,应及时调用下面方法释放相机资源
public final void release()
SurfaceView实例是相机的取景器,SurfaceView是一种特殊的视图,可直接将要显示的内容渲染输出到设备的屏幕上。
简单地说,SurfaceView可以完成单位时间内大量界面变化的需求,比如视频播放器,游戏画面,照相机取景等。
SurfaceView的内部利用了双缓冲机制实现了画面的快速刷新,所谓的双缓冲机制,就是利用SurfaceView内部包含的两个子线程(假设为Thread A和Thread B)交替工作,其工作示意图如下:
Thread A : 解码图像—>前台显示—>解码图像—>前台显示 。。。
Thread B : —(空)—- 解码图像—>前台显示—>解码图像 。。。
也就是说,Thread A 和Thread B交替工作,当A线程解码图像的时候,B线程在同一时刻把刚刚解码的图像切换至UI线程(主线程)并进行显示,以保证在任意时刻总有解码完毕的图像显示在界面上,这样,就达到了流畅的视频播播放效果。
在SurfaceView中了实现SurfaceHolder.Callback接口,SurfaceHolder是用户与Surface对象联系的纽带,而Surface对象代表原始像素数据的缓冲区。
当SurfaceView出现在屏幕上时,会创建Surface;当SurfaceView从屏幕上消失时候,Surface随即被销毁。Surface不存在时候,必须保证没有任何内容要在它上面绘制。
不同于其他视图对象,SurfaceView及其协同工作对象都不会自我绘制内容,对于任何想将内容绘制到Surface缓冲区的对象,我们将其称之为Surface客户端,Camera对象就是一个Surface客户端。
也就是说,只有当Surface对象创建完成后,Surface的客户端(如Camera)才能在Surface的缓冲区绘制,而当Surface不存在时,Surface的缓冲区不能有任何绘制的内容。SurfaceHolder.Callback接口就是用于监听Surface的生命周期事件,以便控制Surface与其客户端协同工作,其三个回调方法如下(当Surface和其客户端关联/不再关联时回调):
public abstract void surfaceCreated(SurfaceHolder holder)
//Surface首次出现在屏幕上时调用,该方法通知Surface客户端(如Camera),有多大的绘制区域可以使用
public abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
//SurfaceView从屏幕移除时,Surface也被随即销毁,通过该方法通知Surface的客户端(如Camera)停止使用Surface
public abstract void surfaceDestoryed(SurfaceHolder holder)
下面三个Camera中的方法用于响应相应的Surface生命周期事件:
public final void setPreviewDisplay(SurfaceHolder holder)
//用于在Surface上绘制,在surfaceChanged()中调用
public final void startPreview()
//用于停止在Surface上绘制,在surfaceDestroyed()中调用
public final void stopPreview()
使用Intent调用系统相机拍照
实现一个相机拍照功能,最简单的方式是使用隐式Intent调用系统自带的照相机拍照功能,要实现此功能,需要为Intent设置一个action和一个extra,代码片段如下:
//在Intent中设置action为MediaStore.ACTION_IMAGE_CAPTURE,表示启动一个包含照相功能的组件
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
//为Intent添加一个extra,键为MediaStore.EXTRA_OUTPUT,值为SD卡根目录。表示将相机捕捉的图像保存在该位置
file = new File(Environment.getExternalStorageDirectory(),
System.currentTimeMillis() + ".jpg")
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file))
在回调方法onActivityResult方法中,将目标intent携带的返回信息(即拍摄的照片)显示在imageView中:
mImageView.setImageURI(Uri.fromFile(file))
为捕捉的图像添加预览功能:
Intent intent = new Intent(Intent.ACTION_VIEW)
intent.setDataAndType(Uri.fromFile(file), "image/*")
startActivity(intent)
使用Camera和SurfaceView自定义照相机
根据第一部分的介绍,下面将简介一个自定义的照相机应用。
实现步骤:
- 检测设备上是否有摄像头;
- 检测设备版本,根据版本型号调用相应的open方法,以创建Camera对象;
- 为Camera对象设置Parameter参数;
- 定制实现了SurfaceHolder.Callback接口的SurfaceView类,并在回调方法中连接/断开Surface客户端(本例中为Camera);
- 将定制的SurfaceView添加至承载布局容器中;
- 调用Camera的takePicture方法进行拍照。
下面将对如上所述的各个步骤以代码的方式结合介绍:
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
return true;
} else {
return false;
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD)
{
mCamera = Camera.open(0);
}
else
{
mCamera = Camera.open();
}
//第三步,为Camera设置参数
Parameters parameters = camera.getParameters()
// 设置闪光灯强制打开
parameters.setFlashMode(parameters.FLASH_MODE_AUTO)
// 设置白平衡,WHITE_BALANCE parameters.setWhiteBalance(Parameters.WHITE_BALANCE_AUTO)
// 设置照片颜色特效,EFFECT parameters.setColorEffect(parameters.EFFECT_SEPIA)
// 设置拍摄照片的尺寸
parameters.setPictureSize(1280, 720)
// 设置照片的预览尺寸
parameters.setPreviewSize(1280, 720)
// 设置照片的质量
parameters.setJpegQuality(100)
// Android 2.2及以后(>=API 8) 0 水平 90垂直方向
camera.setDisplayOrientation(90)
SurfaceHolder holder = mSurfaceView.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new SurfaceHolder.Callback()
{
@override
public void surfaceCreated(SurfaceHolder holder)
{
try
{
if(mCamera != null)
{
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}
}
catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
@override
public void surfaceDestroyed(SurfaceHolder holder) {
if(mCamera != null)
{
mCamera.stopPreview();
}
}
@override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (holder.getSurface() == null) {
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
}
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (Exception e) {
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
});
- 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
- 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
CustomSurfaceView preview = new CustomSurfaceView(this, mCamera);
mContainLayout.addView(preview);
第六步:调用下面方法实现拍摄一张照片
public final void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Callback.PictureCallback jpeg)
- 参数1:Camera.ShutterCallback
在接口Camera.ShutterCallback中的回调方法
public abstract void onShutter();
会在相机捕获图像时调用,但此时,图像还未处理完成,所以,在该回调方法中,一般会显示一个进度条,告知用户保存图片的处理进度。
- 参数2和参数3:Camera.PictureCallback
在接口Camera.PictureCallback中的回调方法
public abstract void onPictureToken(byte[] data, Camera camera)
参数2和参数3接口都会回调上述方法,但参数2一般是在加工处理原始图像数据且没有存储之前;而参数3回调是在JPEG版本的图像可用时。
mCamera.takePicture(new ShutterCallback() {
@Override
public void onShutter() {
Toast.makeText(getApplicationContext(),"点击快门", 0).show();
}
}, null,
new PictureCallback() {
@Override
public void onPictureTaken(byte[] data,Camera camera) {
try {
File file = new File(Environment .getExternalStorageDirectory(), SystemClock.uptimeMillis()+ ".jpg");
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
Toast.makeText(getApplicationContext(),"拍照成功", 0).show();
mCamera.startPreview();
}
catch (Exception e) {
e.printStackTrace();
}
}
});
- 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
- 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
注意事项
- 使用相机拍摄照片与访问用户的外置存储卡都涉及侵犯用户隐私,故需要在AndroidManifest中向系统声明权限:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 使用uses-feature标签告知应用商店(如Google Play)本应用将使用设备的摄像头,若设备上没有摄像头,设备将无法在应用商店(如Google Play)中搜索到该应用:
<uses-feature android:name="android.hardware.camera" />
使用Intent调用系统相机录像
与调用系统相机拍照相仿,使用隐式Intent调用系统相机录像只是Intent的action不同:
//设置action为MediaStore.ACTION_VIDEO_CAPTURE,表示启动一个可以进行录像组件
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE)
file = new File(Environment.getExternalStorageDirectory(), SystemClock.uptimeMillis() + ".mp4")
// 创建一个文件存储file,并设置文件名,设置uri为存储录像的路径
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file))
startActivityForResult(intent, 200)
在onActivityResult方法中接收启动的目标activity的返回信息:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 200:
Toast.makeText(MainActivity.this, "录像成功", 0).show();
break;
default:
break;
}
}
启动系统播放器,播放拍摄的视频
Intent intent = new Intent(Intent.ACTION_VIEW)
intent.setDataAndType(Uri.fromFile(file), "video/*")
startActivity(intent)
自定义摄像机功能如下:
- 检测设备上是否有摄像头;(此步在下面的示例代码中省略)
- 检测设备版本,根据版本型号调用相应的open方法,以创建Camera对象;(此步在下面的示例代码中省略)
- 定制实现了SurfaceHolder.Callback接口的SurfaceView类,并在回调方法中连接/断开Surface客户端(本例中为Camera);
- 解锁摄像头(Camera.unlock());
- 创建MediaRecorder对象,并为其设置参数;
- 依次调用MediaRecorder.prepare()和MediaRecorder.start()方法开始录像;
- 当触发终止录像操作时,分别调用MediaRecorder.stop()、MediaRecorder.release()释放MediaRecorder对象,并将对象置为空(null),最后调用Camera.lock()锁定Camera。
public class MainActivity extends Activity {
private Button bt_take_video;
private SurfaceView sView;
private Camera camera;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_take_video = (Button) findViewById(R.id.bt_take_video);
sView = (SurfaceView) findViewById(R.id.id_sv);
sView.getHolder().addCallback(new MycallBack());
bt_take_video.setOnClickListener(new OnClickListener() {
private MediaRecorder recorder;
@Override
public void onClick(View v) {
Button button = (Button) v;
if ("开始录像".equals(button.getText())) {
button.setText("暂停录像");
camera.unlock();
recorder = new MediaRecorder();
recorder.setCamera(camera);
recorder.setAudioSource(AudioSource.MIC);
recorder.setVideoSource(VideoSource.CAMERA);
recorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_HIGH));
recorder.setOutputFile("/mnt/sdcard/"
+ System.currentTimeMillis() + ".mp4");
recorder.setPreviewDisplay(sView.getHolder().getSurface());
try {
recorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
recorder.start();
} else {
button.setText("开始录像");
if (recorder != null) {
recorder.stop();
recorder.release();
recorder = null;
camera.lock();
}
}
}
});
}
private class MycallBack implements SurfaceHolder.Callback {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera = Camera.open();
camera.setPreviewDisplay(sView.getHolder());
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = 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
- 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
注意事项
在AndroidManifest中设置应用需要访问的系统权限:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />