Android 保存图片到图库

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

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

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

发布了11 篇原创文章 · 获赞 13 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/similing/article/details/102659968