Camera有两种使用方式:
1. 使用系统相机
2. 使用自定义相机
Camera的需要的权限
使用Camera需要在清单文件中声明一些权限,有了这些权限才允许使用相机硬件和相关相机功能。
Camera Permission:
<uses-permissionandroid:name="android.permission.CAMERA" />
要使用相机设备,就必须要有这个权限。
注意:如果你通过一个Intent来使用相机的话,就不需要这个权限了。
Camera Features:
您的应用程序还必须声明使用相机的功能。
例如:<uses-feature android:name="android.hardware.camera" />
以下是相机功能列表:
1.
android.hardware.camera
作用:应用程序使用摄像头,如果有多个摄像头默认使用后置摄像头。
2.
android.hardware.camera.autofocus
作用:子功能。应用程序使用设备的相机的自动对焦能力。
3.
android.hardware.camera.flash
作用:子功能。应用程序使用该设备的闪光灯。
4.android.hardware.camera.front
作用:子功能。该应用程序使用前面的摄像头在设备上。
5. android.hardware.camera.any
作用:该应用程序使用在任何方向上的至少一个摄像头,或一个外部相机设备,如果一个被连接。使用这个优先android.hardware.camera如果后置的摄像头是不需要的。(应该意思是如果后置摄像头没有被使用的情况下,优先使用后置摄像头)。
注意:
在清单文件中增加CAMERAFEATURES会导致Gooplay阻止应用程序安装到没有摄像头的设备上或不支持您指定功能的设备上。
<uses-featureandroid:name="android.hardware.camera"android:required="false" />
注:如果你的应用程序可以使用一个摄像头或具有摄像头功能从而进行适当的操作,但不需要它,你应该在清单中指定android:required="false"
6. 存储权限:如果您的应用程序将图像或视频保存到设备的外部存储(SD卡),您还必须在清单中指定此权限。
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" />
7. 录音权限:用于录制音频视频捕获,您的应用程序必须请求音频捕获权限。
<uses-permissionandroid:name="android.permission.RECORD_AUDIO" />
8. 位置权限:如果您的应用程序产生的图像具有全球定位信息,您必须请求位置权限。
<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION" />
使用已经存在的Camera
一个快速的方法来拍照或录像就是使用一个意图来调用Android手机上的相机应用程序来拍照或摄像。
使用相机意图需要通过以下步骤:
1. 创建一个意图,请求图像或视频:
MediaStore.ACTION_IMAGE_CAPTURE :拍摄照片的意图。
MediaStore.ACTION_VIDEO_CAPTURE :录像的意图。
2. 使用startactivityforresult()方法执行相机的意图。
3. 建立一个onactivityresult()方法,这个方法可以在当用户拍摄完一张照片或一段视频时,从相机意图中接受回调和数据。
捕捉图像的意图
一个图像捕获的意图可以包含以下信息:
MediaStore.EXTRA_OUTPUT:如果要保存图片的话,这个设置需要一个URI来指定的路径和文件名。这个设置是可选的,但强烈建议。如果你不指定这个值,相机应用程序保存请求的图片具有默认名称的默认位置,在返回的意图的getdata()字段指定。
下面是如何创建图像意图并执行它的例子:
在这个例子中getoutputmediafileuri()法是指在保存媒体文件的示例,这是一个自定义的方法,用来保存媒体文件。
private static final intCAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private Uri fileUri;
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// create Intent to take a picture and return control to the callingapplication
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
fileUri =getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image filename
// start the image capture Intent
startActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
当startactivityforresult()方法被执行,用户看到一个相机应用界面。用户完成拍照后(或取消),用户界面返回到你的应用,你必须拦截onactivityresult()方法接收意图的结果和继续执行你的应用。有关如何接收已完成的意图的信息,请参见接收相机的意图结果。
保存多媒体文件:
由用户创建的媒体文件,如图片和视频应保存到设备的外部存储目录(SD卡),
来节省系统空间,并允许用户可以以其他的方式访问他们。
作为开发者要知道的两个标准的存放多媒体文件的位置。
a) Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
这个方法返回一个谷歌推荐的,标准的,共享的保存图片和视频的位置。此目录是共享的(公共),因此其他应用程序可以很容易地发现,读取,更改和删除该位置保存的文件。如果你的应用被用户卸载,媒体文件保存到该位置将不会被删除。为了避免干扰用户现有的图片和视频,您应该为您的应用程序在该目录下的媒体文件创建一个子目录,如下面的代码示例所示。此方法可在安卓2.2(原料药8),在早期的原料药的版本,以相同的要求,见保存共享文件。
保存可以与其他应用程序共享的文件:
一般而言,用户可以通过应用程序获得的新文件应该被保存到设备上的“公共”位置,其他的应用程序可以访问他们,用户可以很容易地从设备中复制他们的位置。当你这样做的时候,你应该使用一个共享的公共目录,如音乐,图片,和铃声。
getExternalStoragePublicDirectory():得到一个代表公共目录的文件。通过它可以设置你想要的目录类型,如directory_music,directory_pictures,directory_ringtones,或其他(分类存放)。
例如,这里的一个方法,创建一个新的相册目录的公共图片目录:
public FilegetAlbumStorageDir(String albumName) {
// Get the directory for the user's publicpictures directory.
File file = newFile(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES),albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory notcreated");
}
return file;
}
b) Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES):此方法返回一个用于保存与你的应用程序相关的图片和视屏资源的位置。如果你的应用程序被卸载,任何保存在这个位置的文件都会被删除。其他应用程序依然可以读取、更改和删除它们。
下面的代码演示如何为多媒体文件创建一个文件或一个URI,当通过意图调用相机设备。
public static final int MEDIA_TYPE_IMAGE =1;
public static final int MEDIA_TYPE_VIDEO =2;
/** Create a file Urifor saving an image or video */
private static UrigetOutputMediaFileUri(int type){
return Uri.fromFile(getOutputMediaFile(type));
}
/** Create a File forsaving an image or video */
private static File getOutputMediaFile(inttype){
// To be safe, you should check that the SDCard ismounted
// usingEnvironment.getExternalStorageState() before doing this.
FilemediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES),"MyCameraApp");
// This locationworks best if you want the created images to be shared
// betweenapplications and persist after your app has beenuninstalled.
// Create thestorage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a mediafile name
String timeStamp = newSimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
}else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".mp4");
}else {
return null;
}
return mediaFile;
}
注:
getexternalstoragepublicdirectory()在Android 2.2(API Level 8可用)或更高版本中可用。
getexternalstoragedirectory()可用于更早的版本。
当startactivityforresult()方法被执行,用户看到一个相机应用界面。当用户完成照相或取消操作之后,用户界面返回。你必须拦截onactivityresult()方法并接受意图的结果和继续执行你的应用。
如何接收相机的意图结果?
一旦你已经构建并执行了一个图像或视频的相机意图,你的应用程序必须被配置为接收的意图的结果。本节将向您展示如何从一个相机的意图中截取回调,这样您的应用程序可以进一步处理所捕获的图像或视频。
为了接收意图的结果,你必须在Activity中重写onActivityResult方法并启动意图。为了接收意图的结果,你必须在活动开始的意图推翻onactivityresult()。下面的示例演示如何重写onactivityresult()捕捉到的图像的相机或摄像机意图。
private static final intCAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private static final intCAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
@Override
protected void onActivityResult(intrequestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
// Image captured and saved to fileUrispecified in the Intent
Toast.makeText(this, "Image saved to:\n" +
data.getData(),Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
//User cancelled the image capture
} else {
//Image capture failed, advise user
}
}
if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
//Video captured and saved to fileUri specified in the Intent
Toast.makeText(this, "Video saved to:\n" +
data.getData(),Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
//User cancelled the video capture
} else {
//Video capture failed, advise user
}
}
}
一旦您的活动收到一个成功的结果,所捕获的图像或视频可在指定的位置为您的应用程序访问。
/******************************************************************************
******************************************************************************/
//实例化一个intent,并指定action
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//指定一个图片路径对应的file对象
uri = Uri.fromFile(ImageUtil.getImageFile());
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//启动activity
startActivityForResult(intent, REQUEST_CODE_CAMERA);
但是在onActivityResult(intrequestCode, int resultCode, Intent data)代码中得到的data总为null?**?????????
原因分析:
于是我查看了Android系统框架Camera应用程序,找到了关于系统照相机如何处理返回值data问题!
默认情况下,即不需要指定intent.putExtra(MediaStore.EXTRA_OUTPUT,uri);照相机有自己默认的存储路径,拍摄的照片将返回一个缩略图。如果想访问原始图片,可以通过dat extra能够得到原始图片位置。即,如果指定了目标uri,data就没有数据,如果没有指定uri,则data就返回有数据!现在想想,这种设计还是很合理的!
protected void onActivityResult(intrequestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_CAMERA:
if (resultCode == RESULT_OK) {
if(data !=null){ //可能尚未指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//返回有缩略图
if(data.hasExtra("data")){
Bitmap thumbnail =data.getParcelableExtra("data");
//得到bitmap后的操作
}
}else{
//由于指定了目标uri,存储在目标uri,intent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
// 通过目标uri,找到图片
// 对图片的缩放处理
// 操作
}
}
}
}
Android系统照相机部分关键源码:
// First handle the no crop case -- just return the value. If the
// caller specifies a "save uri" then write the data toit's
// stream. Otherwise, pass back a scaled down version of the bitmap
// directly in the extras.
if (mSaveUri != null) { //存在mSaveUri,即指定了目标uri
OutputStream outputStream = null;
try {
outputStream = mContentResolver.openOutputStream(mSaveUri);
outputStream.write(data);
outputStream.close();
setResult(RESULT_OK); //直接返回RESULT_OK,并没有指定intent
finish();
} catch (IOException ex) {
// ignore exception
} finally {
Util.closeSilently(outputStream);
}
}else {
Bitmap bitmap = createCaptureBitmap(data);
// 返回RESULT_OK,并包含一个Intent对象,其中Extra中科key为data,value为一个bitmap
setResult(RESULT_OK, newIntent("inline-data").putExtra("data", bitmap));
finish();
}
/***************************************************************************************************************************************************************/