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 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; }}
更多相关文章
- Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存
- Android常用控件--TimePickerDialog(时间选择对话框)
- ProgressBar:自定义旋转图片
- Android 定时器实现图片的变换
- Android获取系统时间方法的总结
- android studio 小技巧之 图片预览
- android获取网络图片的用法 BitmapFactory.decodeByteArray