这一节讲述如何利用已有的拍照应用获取一张照片。
假设你实现了大量的天气服务来绘制一张全球气象图,这个图里面的天气图片都是来自你的客户端APP拍照获取,收集图片只是很小的一部分工作,对于你的APP来说。所以在拍照获取图片方面,要尽可能的最简单化,做最少的工作,不需要完全去重新实现一个相机,绝大部分android设备都会至少有一个拍照应用。这一节里面就学习,如何利用已经存在的拍照应用获取照片。
请求拍照权限
如果你的APP要利用拍照获取图片,那么就要在GooglePlay里面限制,仅仅针对那些有相机的设备才能看到。要声明你的APP需要相机,只需要在manifest里面声明即可,利用<uses-feature>
来声明:
<manifest ... >
<uses-feature android:name="android.hardware.camera" />
...
</manifest ... >
如果你的APP使用了它,但是并不是说必须用camera来获取一张图片,那么在这个标签里面加入android:required="false"。这样做的的话GooglePlay允许那些没有摄像头的设备也来下载这个APP。然后你需要在运行的时候,通过hasSystemFeature(PackageManager.FEATURE_CAMERA)去检测设备的相机是可用。如果相机不可用,那么你就要隐藏你的APP的拍照特性。
利用相机APP获取照片
android利用intent来授权另外一个app来做你希望它做的事情。这个过程包括三块:intent自身的创建,调用方法来通过intent启动一个外部拍照Activity,处理拍照返回的图像数据。
下面的的方法就是利用intent来拍一张照片:
private void dispatchTakePictureIntent(int actionCode) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, actionCode);
}
利用这个代码,你的APP可以让另外一个相机应用来执行你要求的拍照动作。当然,如果没有APP能够响应这个intent,你的APP会没有任何效果,拍照会失败。下面的代码可以检测是否有app能够处理这个intent.
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
如果返回值true,说明至少有一个应用可以处理这个intent.
查看照片
如果你的APP不是仅仅想拍照照片那么简单,也许你会想从相机应用得到拍照获取的照片,然后对它进行一些处理。
android相机应用把照片数据放到一个intent里面,然后传递给了onActivityResult()函数,作为一个extra数据,是一个简单Bitmap,使用”data” 这个extra属性获取。下面的代码就是获取到照片,然后显示到一个ImageView.
private void handleSmallCameraPhoto(Intent intent) {
Bundle extras = intent.getExtras();
mImageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(mImageBitmap);
}
注意:从data获取到的这个缩略图,很适合做为一个图标,不适合做wie其他,想要获得一个高分辨率的全图,需要更多的工作。
保存照片
android的相机应用在你可以它一个存储路径的时候,会获取到一张全幅的图像。你必须要提供一个路径,包括:存储盘,文件夹,文件名。
下面的方法是简单的一种获取存储照片路径的方法,但是仅仅针对android2.2(API 8)和之上的设备版本。
storageDir = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
),
getAlbumName()
);
对于早期的API,你要自己提供路径名称:
storageDir = new File (
Environment.getExternalStorageDirectory()
+ PICTURES_DIR
+ getAlbumName()
);
注意: PICTURES_DIR这个是的是
Pictures/
,是外部或者内部存储器上标准的用来分享照片的位置。
设置文件名称
如前面提到的,图像文件的位置是有设备环境决定的。你要做的就是要选择一种没有冲突的照片命名方案。你也许想把文件路径名字保存起来,供后面使用。下面是一种解决办法:
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp =
new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
File image = File.createTempFile(
imageFileName,
JPEG_FILE_SUFFIX,
getAlbumDir()
);
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
添加文件名到另外一个Intent
当有了一个文件保存的位置,那么就把这个位置通话intent传递给拍照应用。
File f = createImageFile();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
把照片添加到图库
当通过intent请求拍照到一张图片的时候,你应该是知道你的图片被保存到哪里,因为已经指定了路径。对于其他的应用来说,最简单的方法获取到你的照片是通过Media Provider来获取。
下面的代码就演示了如何让你的照片和系统的多媒体扫描相关联,来把你的照片添加到Media Provider的数据库。让其对android 图库类的应用可见或者是其他类似的应用。
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);
}
编码一个缩放的图像
管理多种全尺寸的照片,对于有限的内存来说是很费力的。如果你发现你的程序在显示一部分图片之后,内存溢出了,你可以减少那个已经被缩放到符合到比例的jepg展开时所用的内存堆。下面的代码展示了这个技术:
private void setPic() {
// Get the dimensions of the View
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.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 image
int 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);
}