这节课教我们如何通过将工作委托给设备上的另一个摄像头应用来拍摄一张照片。(如果你想构建自己的相机功能,请参见控制相机。)
如果整合照片只是你应用的小部分时,你想要最低成本拍照,而不是重新实现一个拍照功能。令人高兴的是,大多数android设备已经安装了至少一个摄像头应用程序。在这节课中,你要学习如何让它为你拍照。
请求相机功能
如果拍照是你应用程序的一个基本功能,那么将其在谷歌应用市场上的可见性限制是,有摄像头的设备。为了宣传您的应用程序依赖于有一个摄像头,在您的清单文件中放置一个< uss -feature>标记:
<manifest ...>
<uses-feature android:name="android.hardware.camera"android:required="true"/>
</manifest >
如果您的应用程序使用,但不需要摄像头来运行,则设置android:required="false"。通过这样做,谷歌Play将允许没有摄像头的设备下载应用程序。然后,您有责任通过调用hasSystemFeature(packagemanagemanager . feature_camera)检查相机在运行时的可用性。如果没有摄像头,你应该禁用你的摄像头功能。
用相机应用拍照
Android中将操作委托给其他应用程序的方式是调用一个意图(意图:用来描述您想要做的事情)。这个过程包括三个部分:意图本身、启动外部活动的调用,以及当焦点返回到活动时处理图像数据的代码。
这是一个调用捕获照片意图的函数。
static final int REQUEST_IMAGE_CAPTURE = 1;
private void dispatchTakePictureIntent() {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult
(takePictureIntent, REQUEST_IMAGE_CAPTURE);}}
请注意,startActivityForResult()方法受到调用resolveActivity()条件的保护(条件限制保护),该条件返回能够处理意图的第一个活动组件。执行这个检查是很重要的,因为如果您调用startActivityForResult()使用了任何应用程序都无法处理的意图,您的应用程序将崩溃。所以只要结果不是空的,使用意图是安全的。
获取缩略图
如果拍照这个简单的壮举并不是你的应用野心的顶点,那么你可能想要从相机应用中取回照片,然后用它做点什么。
Android Camera应用程序将照片编码为onActivityResult()返回的意图的Extra信息中的一个小位图,对应键为“data”。下面的代码检索此映像并将其显示在ImageView中。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(imageBitmap);
}
}
注意:这个来自“data”的缩略图当做一个图标icon可能ok,但不能再多要求了。处理一个完整的图像需要更多的工作。
保存全尺寸照片
keywords:full-size
如果你给它一个文件来保存,Android Camera应用程序会保存全屏照片。你必须提供一个完全合格的文件名称,以便相机应用程序保存照片。
通常,用户用设备摄像头拍摄的任何照片都应该保存在设备的公共外部存储中,以便所有应用程序都能访问这些照片。共享照片的适当目录由getExternalStoragePublicDirectory()提供,并带有DIRECTORY_PICTURES参数。由于此方法提供的目录在所有应用程序之间共享,因此对其进行读写需要分别具有READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。写入权限隐式地允许读取,所以如果您需要写入外部存储,那么您只需要请求一个权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
但是,如果希望照片只对应用程序私有,可以使用getExternalFilesDir()提供的目录。在Android 4.3和更低的版本中,写入这个目录也需要WRITE_EXTERNAL_STORAGE权限。从Android 4.4开始,不再需要权限,因为其他应用程序无法访问该目录,所以您可以通过添加maxSdkVersion属性,只在Android的较低版本上请求权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"android:maxSdkVersion="18"/>
注意:在用户卸载应用程序时,将删除保存在getExternalFilesDir()或getFilesDir()所提供目录中的文件。
确定文件的目录后,需要创建一个抗冲突的文件名。您可能还希望将路径保存到成员变量中以供以后使用。这里有一个方法的示例解决方案,该方法使用日期-时间戳为新照片返回唯一的文件名:
String mCurrentPhotoPath;
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
使用此方法为照片创建文件,您现在可以创建和调用这样的意图:
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
...
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
注意:getUriForFile(context ,string,file)会返回内容:// URI,我们使用 getUriForFile时会导致异常。对于最近针对Android 7.0 (API级别24)和更高的应用程序,跨包边界传递文件:// / URI会导致FileUriExposedException。因此,我们现在提供了一种使用FileProvider存储图像的更通用的方法。
现在,您需要配置FileProvider。在您的应用程序清单中,向应用程序添加一个提供者:
<application>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
</application>
自定义file_paths指定可访问文件路径组件对应于getExternalFilesDir()在调用Environment.DIRECTORY_PICTURES时返回的路径。确保您将com.example.package.name替换为应用程序的实际包名。此外,还要检查FileProvider的文档,以获得除external-path之外可以使用的路径说明符的详细描述。
将照片添加到一个画廊
当您通过一个意图创建一个照片时,您应该知道您的图像位于何处,因为您首先指定了在何处保存它。对于其他人来说,可能让你的照片可访问的最简单的方法是从系统的媒体提供商(相册)那里访问它。
注意:如果您将照片保存到getExternalFilesDir()提供的目录中,媒体扫描器无法访问这些文件,因为它们是应用程序的私有文件。
下面的示例方法演示了如何调用系统的媒体扫描器将照片添加到媒体提供者的数据库中,使其在Android Gallery应用程序和其他应用程序中可用。
private void galleryAddPic() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
解码一个按比例缩小的图片
在内存有限的情况下,管理多个全尺寸图像是很困难的。如果您发现您的应用程序在仅仅显示了几个映像之后就耗尽了内存,那么可以通过将JPEG扩展到一个内存数组中,使其与目标视图的大小相匹配,从而大大减少使用的动态堆的数量。下面的示例方法演示了这种技术。
private void setPic() {// Get the dimensions of the Viewint targetW = mImageView.getWidth();int targetH = mImageView.getHeight();
// Get the dimensions of the bitmapBitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions
.inJustDecodeBounds = true;BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);int photoW = bmOptions.outWidth;int photoH = bmOptions.outHeight;
// Determine how much to scale down the imageint scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions
.inJustDecodeBounds = false;
bmOptions
.inSampleSize = scaleFactor;
bmOptions
.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
mImageView
.setImageBitmap(bitmap);}
网友评论