最近有个小需求,在移动设备(iOS / Android)中应用调用拍照并把所拍照片存入相册。我原本以为这是一个很简单的事情,不就拍个照然后把照片丢进相册?心想最多100行代码即可解决问题,谁知还是踩中坑。且听我一一分解。
必备权限
<uses -permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses>
<uses -permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses>
<uses -permission android:name="android.permission.CAMERA"></uses>
添加入 AndroidManifest.xml 中
执行拍照
File destDir = new File(m_ImageRootPath); if (!destDir.exists()) { destDir.mkdirs(); } Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(new File(m_ImageRootPath , m_ImageName))); startActivityForResult(intent, 100);
代码中 m_ImageRootPath
为照片所存目录,m_ImageName
为照片名。注意,若拍照不止一张 则照片名字最好每次不一样,否则你打开相册有可能每次都只能看到一张照片。
拍照回调
//------------------------------------------------------------------------- /** * Activity 回调 */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 100) { if (0 == resultCode) { // 拍照失败 } else { // 拍照成功 __SaveImage2Media(); } finish(); } super.onActivityResult(requestCode, resultCode, data); } //-------------------------------------------------------------------------
拍照后的回调,你在网上找到N个版本都差不多的 所以不解释了。重点看下面保存照片到相册
保存照片到相册
private void __SaveImage2Media() { try { __FixMediaDir(); File file = new File(m_ImageRootPath + m_ImageMiddlePath + m_ImageName); Uri uri = Uri.fromFile(file); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(this.getContentResolver().openInputStream(uri), null, options); options.inSampleSize = 4; options.inJustDecodeBounds = false; Bitmap map = BitmapFactory.decodeStream(this.getContentResolver().openInputStream(uri), null, options); String strCapturedImage = MediaStore.Images.Media.insertImage(getContentResolver(), map, null, null); if (null == strCapturedImage || strCapturedImage.isEmpty()) { long dateTaken = System.currentTimeMillis(); String strFilePath = m_ImageRootPath + m_ImageName; ContentValues values = new ContentValues(7); values.put(MediaStore.Images.Media.TITLE, m_ImageName); values.put(MediaStore.Images.Media.DISPLAY_NAME, m_ImageName); values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); values.put(MediaStore.Images.Media.DATA, strFilePath); getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { Uri capturedImage = Uri.parse(strCapturedImage); MediaScannerConnection.scanFile(this, new String[] { __GetFilePathByContentResolver(this, capturedImage) }, null, null); } } catch (Exception e) { // 保存出错 } } //------------------------------------------------------------------------- private void __FixMediaDir() { File sdcard = Environment.getExternalStorageDirectory(); if (sdcard != null) { File mediaDir = new File(sdcard, "DCIM/Camera"); if (!mediaDir.exists()) { mediaDir.mkdirs(); } } } //------------------------------------------------------------------------- private String __GetFilePathByContentResolver(Context context, Uri uri) { if (null == uri) { return null; } Cursor c = context.getContentResolver().query(uri, null, null, null, null); String filePath = null; if (null == c) { throw new IllegalArgumentException( "Error:" + uri + " is Null"); } try { if ((c.getCount() != 1) || !c.moveToFirst()) { } else { filePath = c.getString( c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)); } } finally { c.close(); } return filePath; }
- 函数 __FixMediaDir 创建默认的相册目录,有些设备这个目录是没有的或者很多时候被误删也正常。
- 函数 __GetFilePathByContentResolver 通过 uri 取图片全路径,用来刷新相册
- 函数 __SaveImage2Media 保存所拍照片到相册
一号坑
在 __SaveImage2Media 函数中刷新相册使用此句:
MediaScannerConnection.scanFile( this, new String[]{__GetFilePathByContentResolver(this,capturedImage)}, null, null);
这句代码的作用是只刷新单个照片,就是我们刚刚拍照所得的那一张。在android老版本中是直接刷新SD卡中全部的,但是那种方法在高版本的android设备中会秒崩。
二号坑
在 __SaveImage2Media 函数中保存图片的部分:
// 第一种 String strCapturedImage = MediaStore.Images.Media.insertImage(getContentResolver(), map, null, null); if (null == strCapturedImage || strCapturedImage.isEmpty()) { // 第二种 long dateTaken = System.currentTimeMillis(); String strFilePath = m_ImageRootPath + m_ImageName; ContentValues values = new ContentValues(7); values.put(MediaStore.Images.Media.TITLE, m_ImageName); values.put(MediaStore.Images.Media.DISPLAY_NAME, m_ImageName); values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); values.put(MediaStore.Images.Media.DATA, strFilePath); getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { Uri capturedImage = Uri.parse(strCapturedImage); MediaScannerConnection.scanFile(this, new String[]{__GetFilePathByContentResolver(this,capturedImage)}, null, null); }
有两种保存图片的方式,第一种是大家普遍使用的,但是经过我测试发现 第一种方式在某些设备(例如三星SM-T810平板电脑)中会永远返回null。所以当返回的字符串为null的时候执行第二种方式。第一种方式直接把照片写入相册 第二种方式直接把照片的信息索引添加入相册。如此便可在大多数设备上成功让照片在相册中显示(虽然并不都是真实保存到相册)
-EOF-
我在这篇文章提了个问题,估计你没看到,厚着脸皮在这里再发一次: http://shadowkong.com/archives/1952