最近有个小需求,在移动设备(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