不知道大家有在保存图片到图库时有这种经历:

图片存了两份一份压缩了一份没有

图库的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 null 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开发者文档去查了一下:链接
找到了几个有关于时间的字段:
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;    }}

更多相关文章

  1. Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存
  2. Android常用控件--TimePickerDialog(时间选择对话框)
  3. ProgressBar:自定义旋转图片
  4. Android 定时器实现图片的变换
  5. Android获取系统时间方法的总结
  6. android studio 小技巧之 图片预览
  7. android获取网络图片的用法 BitmapFactory.decodeByteArray

随机推荐

  1. Android(安卓)Notes 之 Tween动画 (2)自定
  2. Android判断设备是手机还是平板
  3. android时间对话框的核心代码
  4. Android 选择弹框 AlertDialog选择
  5. ubuntn 8.04 编译 android 流程详解
  6. Android framework开发 基本命令
  7. 【Android(安卓)Demo】图片之网格视图(Gr
  8. android中画虚线--.PathEffect类简单认识
  9. 了解SparseArray
  10. Notification framework层的处理流程分析