发布日期: 2021-06-27
文章字数: {{articleLength}}
阅读时长: {{parseInt(articleLength / 800)}} 分
阅读次数: {{read_count}}
## 不知道大家有在保存图片到图库时有这种经历:
#### 图片存了两份一份压缩了一份没有
#### 图库的Recent(最近)里找不到图片
#### 图库时间不对
大家可能在用系统自带的Android插入图库方法:
**MediaStore.Images.Media.insertImage** in android.provider package of API 28
这个方法是系统提供给我们的插入图库的方法。先来看看它的源代码:
```
/**
* Insert an image and create a thumbnail for it.
*
* @param cr The content resolver to use
* @param source The stream to use for the image
* @param title The name of the image
* @param description The description of the image
* @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
* for any reason.
*/
public static final String insertImage(ContentResolver cr, Bitmap source,
String title, String description) {
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, title);//标题
values.put(Images.Media.DESCRIPTION, description);//描述
values.put(Images.Media.MIME_TYPE, "image/jpeg");//图片格式
Uri url = null;
String stringUrl = null; /* 返回值 */
try {
url = cr.insert(EXTERNAL_CONTENT_URI, values);//将刚刚创建的ContentValues插入图库
if (source != null) {
OutputStream imageOut = cr.openOutputStream(url);
try {
source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);//实际写入的图片
} finally {
imageOut.close();
}
//下面是生成缩略图过程,省略
long id = ContentUris.parseId(url);
// Wait until MINI_KIND thumbnail is generated.
Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
Images.Thumbnails.MINI_KIND, null);
// This is for backward compatibility.
Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
Images.Thumbnails.MICRO_KIND);
} else {
Log.e(TAG, "Failed to create thumbnail, removing original");
cr.delete(url, null, null);
url = null;
}
} catch (Exception e) {
Log.e(TAG, "Failed to insert image", e);
if (url != null) {
cr.delete(url, null, null);
url = null;
}
}
if (url != null) {
stringUrl = url.toString();//返回uri对应的字符串形式
}
return stringUrl;
}
```
概括一下就是这个流程:
**1.从Context获取ContentResolver**
**2.新建ContentValues对象,设置其属性**
**3.将ContentValues对象插入数据库,并获得插入的Uri**
**4.通过Uri写入图片文件**
**5.返回Uri的字符串形式**
#### 但是!我们直接使用这个方法会造成以下两点致命问题:
**1.图片被压缩**
**2.时间不正确,不能进入最近列表**
被压缩问题很明显,在调用InsertImage方法时
```
try {
source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);//实际写入的图片
} finally {
imageOut.close();
}
```
已经以50质量的jpeg格式给你的bitmap压缩一遍了。
时间戳不正确,很明显是ContentValues缺少参数。
因此我去Android开发者文档去查了一下:[链接](https://developer.android.google.cn/reference/android/provider/MediaStore.Images.ImageColumns.html?hl=en)
找到了几个有关于时间的字段:
**DATE_ADDED**
**DATE_MODIFIED**
**DATE_TAKEN**
还有一些有用的字段:
**SIZE** (解决图片大小问题)
**WIDTH**
**HEIGHT**
这些字段找全了插入图片就可以变得很简单了。
由于图片保存是在插入数据库之后,所以我们需要在插入数据库之前计算出文件的大小。因此考虑暂时将文件保存到内存中以计算图片文件大小:
```
ByteArrayOutputStream stream = new ByteArrayOutputStream(1920 * 1920);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
long size = stream.size();
stream.close();
```
最终我模拟了整个保存到图库的过程,流程如下:
1.保存图片到内存获取图片大小
2.创建ContentValues对象,设置DATA、DISPLAY_NAME、MIME_TYPE、DATE_ADDED、DATE_MODIFIED、DATE_TAKEN、SIZE、WIDTH、HEIGHT字段
3.使用resolver.insert将数据插入数据库并获取Uri
4.根据uri将图片数据写入文件
**至于说网上很多人说要通知图库更新,没有必要,如果你没插入成功你怎么通知都是没有用的,如果你插入成功了打开图库你的图片就会在里面,图库更新会由系统自动完成,无须干预!**
# 最终代码:
```
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
public class GalleryFileSaver {
public static final String PIC_DIR_NAME = "myPhotos"; //在系统的图片文件夹下创建了一个相册文件夹,名为“myPhotos",所有的图片都保存在该文件夹下。
private static File mPicDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), PIC_DIR_NAME); //图片统一保存在系统的图片文件夹中
public static Uri saveBitmapToGallery(final Context mContext, String fileName, Bitmap bitmap) {
OutputStream out = null;
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream(1920 * 1920);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
long size = stream.size();
stream.close();
mPicDir.mkdirs();
String mPicPath = new File(mPicDir, fileName).getAbsolutePath();
ContentValues values = new ContentValues();
ContentResolver resolver = mContext.getContentResolver();
values.put(MediaStore.Images.ImageColumns.DATA, mPicPath);
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
//将图片的拍摄时间设置为当前的时间
long current = System.currentTimeMillis() / 1000;
values.put(MediaStore.Images.ImageColumns.DATE_ADDED, current);
values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, current);
values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, current);
values.put(MediaStore.Images.ImageColumns.SIZE, size);
values.put(MediaStore.Images.ImageColumns.WIDTH, bitmap.getWidth());
values.put(MediaStore.Images.ImageColumns.HEIGHT, bitmap.getHeight());
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (uri != null) {
out = resolver.openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
return uri;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
```