这节课主要讲述怎样利用设备已安装的其他照相应用程序来拍摄照片。
例如您想要实现一个气象服务的APP,用来把全球的天气图片收集在一起,整合这些图片只是您应用程序的一部分工作,但是您又不想编写太复杂的代码来自己控制相机的拍照,幸好,大多数Android设备已至少安装有一个拍照程序,本课程就教您怎么样利用设备自带的拍照应用来拍摄这些图片。
申请相机权限
如果您的应用的基本功能就是拍照,需要在Android Market 中进行可见性限制,只有拥有摄像头的设备才能浏览和下载您的应用程序,那么您需要在manifest 文件的< uses-feature > tag中加入权限限制。
<manifest ... > <uses-feature android:name="android.hardware.camera" /> ... </manifest ... >
如果您的应用在设备没有相机的情况下仍能正常工作,那么可以在该tag中加入android:required="false"。设定以后,所有的设备不管有没有摄像头都可以在Android Market 上面浏览和下载您的应用。但是,您需要自己在运行时检测设备是否有摄像头,调用方法hasSystemFeature(PackageManager.FEATURE_CAMERA)即可,如果相机不可用,那么您就需要禁用应用中与拍照有关的功能模块。
用相机程序拍照
在Andriod系统中,如果您的应用要委托其他的应用帮忙完成一些工作,可以使用Intent,把期望完成的任务和参数列表设置到Intent中,然后通过系统广播出去,最后当其他应用完成工作的时候,您的应用可以收到一个反馈信息,在回调函数中就可以取得任务处理结果。调用相机程序进行拍照,过程与上面描述的类似,有三个关注点,定义拍照的 Intent,启动其他相机应用Activity,处理拍照后的照片数据。
示例代码,启动拍照应用
private void dispatchTakePictureIntent(int actionCode) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(takePictureIntent, actionCode); }
恭喜您,通过如上的代码就可以给您的应用加入使用设备内置应用拍照的功能,当然,如果恰巧设备中没有安装任何的拍照应用,那么您的应用就有可能出错,一个好的解决办法是,在调用上面的代码前首先检查设备中是否有内置拍照应用,示例代码如下:
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; }
查看照片
如果您的应用不仅仅是通过设备内置的拍照应用拍摄照片,还需要对照片进行进一步的变换处理,那么您需要获得原始照片数据,拍照应用会将编码后的相片数据(small Bitmap )放到Intent的"data"键值中,然后通过回调onactivityresult()返回,如下示例代码将返回后的照片数据显示到 ImageView中。
private void handleSmallCameraPhoto(Intent intent) { Bundle extras = intent.getExtras(); mImageBitmap = (Bitmap) extras.get("data"); mImageView.setImageBitmap(mImageBitmap); }
注意:从Intent的"data"键值中取得的缩略图数据适合作为图标icon,如果要处理大的图片数据,您还要做更多的工作。
保存照片
如果您在指定拍照Intent的时候设置了照片的存储路径,那么拍照应用就会把拍摄的照片以文件的形式保存到该路径中,路径要求是绝对路径(如(存储器名SD卡)/(路径名)/(文件名))。
这里有一个获取照片路径的简单办法,仅限于Android 2.2 (API level 8) 及以上。
storageDir = new File( Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES ), getAlbumName() );
在早期的Android版本中,您必须手动指定照片的目录
storageDir = new File ( Environment.getExternalStorageDirectory() + PICTURES_DIR + getAlbumName() );
注意:PICTURES_DIR 实际上就是 Pictures/,external/shared storage(外部/共享存储设备) 上共享照片的标准目录。
图片命名
如上文所述,拍摄的照片会存放在一个标准的图片目录中,您要做的是解决图片的命名冲突,或许您还会设置一个成员变量来保存,以便后续引用该图片,示例如下:
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));
添加照片到Gallery中
通过拍照应用,您可以知道拍摄的照片具体存放在系统的什么位置,因为这是您在Intent中指定的路径,若想让其他的应用也可以知道该照片的存在,那么最好的办法便是利于系统的媒体管理(system's Media Provider)。
示例代码展示了如何调用系统的媒体管理模块(system's media scanner),将您的照片添加到媒体数据库(Media Provider's database)中,这样系统的Gallery APP和其他的APP就能访问该照片了。
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); }
图片解码
在有限的内存设备上面处理全尺寸的大图片是一个需要关注的问题。如果您发现只显示了几张图片后就内存溢出了,您可以动态地调整堆容量,把图片缩放到界面需要展示的ImageView大小,然后映射到一个内存的JPEG数组中。 代码示例展示了如何使用这一技术。
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); }
参考文摘:
http://developer.android.com/training/camera/photobasics.html