Android 用MultiImageSelector实现单图/多图+压缩
来源:互联网 发布:图像压缩编码编程 编辑:程序博客网 时间:2024/06/07 17:03
本文是基于之前写的文章进行了再次整理,感觉以前做的东西现在看的话都是一个推翻的过程,如果你也是有这种感觉的话,这就意味着你正在进步了。之前写的用MultiImageSelector实现上传头像的拍照跟相册中,这里面对于图片选择成功之后,我是没有做压缩的。
最近做项目的时候,公司后台是有对图片的上传进行了一个限制,这就迫使我去查找一些资料。之前有个朋友问我后台也可以做压缩的,为什么要客户端做呢?我当时就呵呵一笑,主要是流量,用户的流量就是最宝贵的,要不然就直接扔给后台做了。
我们先看效果图:
源码下载
步骤一:
在跟牡蛎的build.gradle中添加以下语句:
buildscript { repositories { .... maven { url "https://jitpack.io" } }}然后在app目录下的build.gradle中添加以下语句:
dependencies { //图片选择器 compile 'com.github.lovetuzitong:MultiImageSelector:1.2' //压缩图片 compile 'me.shaohui.advancedluban:library:1.3.5'}步骤二:
在AndroidManifest.xml中加入启动图片选择器
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<activity android:configChanges="orientation|screenSize" android:name="me.nereo.multi_image_selector.MultiImageSelectorActivity" />步骤三:
文件管理类:FileUtil
压缩管理类:CompressUtils
选择/剪切类:PhotoOrCropUtil
1、FileUtil 用于生成日期时间图片作为临时图片
/** * Created by SoBan on 2017/4/6. * Describe: 文件管理器 */public class FileUtil { public static File getFilePath(Context context) { File outDir = null; String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { //判断状态,保存到sd卡中公有目录(根目录)的pictures文件夹下 outDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); } else { if (context != null) { //保存安装包下的文件夹中 outDir = context.getFilesDir(); } } if (!outDir.exists()) { outDir.mkdirs(); } return outDir; } /** * 获取当前时间 * * @return */ public static String getDate() { SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss"); Date curDate = new Date(System.currentTimeMillis());//获取当前时间 return formatter.format(curDate); //转化为字符串 } /** * 获取当前时间图片 * * @param context * @return */ public static File getDateFile(Context context) { return new File(getFilePath(context), getDate() + ".jpg"); }}2、CompressUtils根据Luban.compress去对图片进行压缩处理,压缩的过程中会生成一些带(luban。。。.jpg)的图片这样的话就不会对本来的图片进行覆盖。
/** * @author SoBan * @create 2017/5/23 * https://github.com/shaohui10086/AdvancedLuban * 压缩图片类 * http://blog.csdn.net/youth_never_go_away/article/details/53945332 * CompressUtils:压缩过程中会产生Luban压缩的图片 */public class CompressUtils { private static String TAG = CompressUtils.class.getName(); private static CompressUtils mInstance; public static synchronized CompressUtils getInstance() { if (mInstance == null) { mInstance = new CompressUtils(); } return mInstance; } private CompressUtils() { } /** * @param context * @param maxSize 单位kb * @param photoPathList * @param callBack */ public void compress(final Context context, int maxSize, final List<File> photoPathList, final CompressFinishCallBack callBack) { if ((photoPathList == null || photoPathList.size() == 0) && callBack != null) { callBack.compressFinish(null); return; } final long startTime = System.currentTimeMillis(); if (photoPathList != null && photoPathList.size() > 0) { List<File> tempFiles = new ArrayList<>(); for (int i = 0; i < photoPathList.size(); i++) { tempFiles.add(photoPathList.get(i)); } Luban.compress(context, tempFiles) .putGear(Luban.CUSTOM_GEAR) .setMaxSize(maxSize) .setCompressFormat(Bitmap.CompressFormat.JPEG) .launch(new OnMultiCompressListener() { @Override public void onStart() { if (callBack != null) callBack.compressStart("开始压缩图片"); } @Override public void onSuccess(List<File> fileList) { if (callBack != null) callBack.compressFinish(fileList); final long finishTime = System.currentTimeMillis(); Log.e(TAG, "压缩时间:" + (finishTime - startTime) / 1000); } @Override public void onError(Throwable e) { if (callBack != null) callBack.compressError("部分文件不存在或已被删除"); } }); } } /** * 压缩单张图片 * * @param context * @param maxSize 单位kb * @param file * @param callBack */ public void compress(final Context context, int maxSize, final File file, final CompressFinishCallBack callBack) { compress(context, maxSize, file.getAbsolutePath(), callBack); } /** * 压缩单张图片 * * @param context * @param maxSize 单位kb * @param photoPath * @param callBack */ public void compress(final Context context, int maxSize, final String photoPath, final CompressFinishCallBack callBack) { if (TextUtils.isEmpty(photoPath)) { callBack.compressFinish(null); return; } final long startTime = System.currentTimeMillis(); Luban.compress(context, new File(photoPath)) .putGear(Luban.CUSTOM_GEAR) .setMaxSize(maxSize) .setCompressFormat(Bitmap.CompressFormat.JPEG) .launch(new OnCompressListener() { @Override public void onStart() { if (callBack != null) callBack.compressStart("开始压缩图片"); } @Override public void onSuccess(File fileList) { callBack.compressFinish(fileList); final long finishTime = System.currentTimeMillis(); Log.e(TAG, "压缩时间:" + (finishTime - startTime) / 1000); } @Override public void onError(Throwable e) { if (callBack != null) callBack.compressError("压缩图片失败"); } }); } public interface CompressFinishCallBack<T> { /** * 开始压缩图片 * * @param msg 提示 */ void compressStart(String msg); /** * 图片压缩完成 * * @param files */ void compressFinish(T files); /** * 图片压缩失败 * * @param msg 提示 */ void compressError(String msg); }}3、上面两个类都是铺垫,主要我们要用的是PhotoOrCropUtil这个类,来启动拍照/单选+剪切、多选
/** * Created by SoBan on 2017/5/23 * Describe:拍照/单选+剪切、多选 */public class PhotoOrCropUtil { private static final int REQUEST_CAREMA = 1001; private static final int REQUEST_MULTI = 1002; private static final int REQUEST_SINGLE = 1003; private static final int PHOTO_REQUEST_CUT = 1004; private File tempFile = null;//临时图片 private Uri imageUri = null; //多选时存放已选中的图片路径的list private ArrayList<String> selectList; private Context mContext; private PhotoOrCropListener mListener; //是否剪切图片 private boolean isCrop = false; public PhotoOrCropUtil(Context context) { selectList = new ArrayList<>(); mContext = context; } /** * 开启相册,用途:多选图片 */ public void openGalleryMuti(int maxsize) { Intent intent = new Intent(mContext, MultiImageSelectorActivity.class); // 是否显示拍摄图片 intent.putExtra(MultiImageSelectorActivity.EXTRA_SHOW_CAMERA, true); // 最大可选择图片数量 intent.putExtra(MultiImageSelectorActivity.EXTRA_SELECT_COUNT, maxsize); // 选择模式 intent.putExtra(MultiImageSelectorActivity.EXTRA_SELECT_MODE, MultiImageSelectorActivity.MODE_MULTI); // 默认选择 if (selectList != null && selectList.size() > 0) { intent.putExtra(MultiImageSelectorActivity.EXTRA_DEFAULT_SELECTED_LIST, selectList); } ((Activity) mContext).startActivityForResult(intent, REQUEST_MULTI); } /** * 开启相册,用途:单选图片 * * @param isCrop 剪切图片 */ public void openGallerySingle(boolean isCrop) { this.isCrop = isCrop; Intent intent = new Intent(mContext, MultiImageSelectorActivity.class); // 是否显示拍摄图片 intent.putExtra(MultiImageSelectorActivity.EXTRA_SHOW_CAMERA, true); // 选择模式 intent.putExtra(MultiImageSelectorActivity.EXTRA_SELECT_MODE, MultiImageSelectorActivity.MODE_SINGLE); ((Activity) mContext).startActivityForResult(intent, REQUEST_SINGLE); } /** * 开启拍照,用途:单选图片 * * @param isCrop 剪切图片 */ public void openCamera(boolean isCrop) { this.isCrop = isCrop; createTempFile(); // 激活相机 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile)); // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CAREMA ((Activity) mContext).startActivityForResult(intent, REQUEST_CAREMA); } /** * 剪切图片,只对单张 */ private void crop(Uri uri) { createTempFile(); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("scale", true); intent.putExtra("return-data", false); // 从文件中创建uri intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile)); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); // no face detection // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CUT ((Activity) mContext).startActivityForResult(intent, PHOTO_REQUEST_CUT); } public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_SINGLE) { // 从相册返回的数据 if (data != null) { // 得到图片的全路径 selectList = data.getStringArrayListExtra(MultiImageSelectorActivity.EXTRA_RESULT); StringBuilder sb = new StringBuilder(); for (String p : selectList) { sb.append(p); } imageUri = Uri.parse("file://" + sb.toString()); try { if (isCrop) { crop(imageUri); } else { tempFile = new File(new URI(imageUri.toString())); setCallbackData(); } } catch (URISyntaxException e) { e.printStackTrace(); } } else { mListener.onError("取消选择"); } } else if (requestCode == REQUEST_CAREMA) { if (resultCode == ((Activity) mContext).RESULT_OK) { //拍照返回的数据 imageUri = Uri.fromFile(tempFile); if (isCrop) { crop(imageUri); } else { setCallbackData(); } } else { mListener.onError("取消选择"); } } else if (requestCode == PHOTO_REQUEST_CUT) { // 从剪切图片返回的数据 if (resultCode == ((Activity) mContext).RESULT_OK) { setCallbackData(); } else { imageUri = null; } } else if (requestCode == REQUEST_MULTI) { if (data != null) { // 得到图片的全路径 selectList = data.getStringArrayListExtra(MultiImageSelectorActivity.EXTRA_RESULT); mListener.onSuccess(getFileList()); } else { mListener.onError("取消选择"); } } } /** * 获取多选的图片列表,未做压缩 * @return */ private List<File> getFileList() { List<File> files = new ArrayList<>(); for (String path : selectList) { files.add(new File(path)); } return files; } private void setCallbackData() { if (imageUri != null) { if (tempFile == null || tempFile.exists() == false) { mListener.onError("图片获取失败"); return; } mListener.onSuccess(tempFile); } } /** * 请求上传图片前调用,压缩图片5kb * * @param file */ public void compressPic(File file, CompressUtils.CompressFinishCallBack<File> callBack) { CompressUtils.getInstance().compress(mContext, 5, file, callBack); } public void compressPic(List<File> files, CompressUtils.CompressFinishCallBack<List<File>> callBack) { CompressUtils.getInstance().compress(mContext, 5, files, callBack); } /** * 创建临时文件 * * @return */ private void createTempFile() { tempFile = FileUtil.getDateFile(mContext); } public void setPhotoOrCropListener(PhotoOrCropListener listener) { mListener = listener; } public interface PhotoOrCropListener { void onSuccess(File file); void onSuccess(List<File> files); void onError(String error); }}上面三个类中,都有很多注释,应该看是可以看得懂的,这就不进行详说了。
步骤四:
上面的写的七七八八了,这个时候最需要的就是demo怎么写,怎么用,下面就举个例子说说。
1、demo类:MainActivity.class
public class MainActivity extends Activity { @Bind(R.id.camera) Button camera; @Bind(R.id.single_gallery) Button singleGallery; @Bind(R.id.multi_gallery) Button multiGallery; @Bind(R.id.image_layout) LinearLayout imageLayout; private PhotoOrCropUtil photoOrCropUtil; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); photoOrCropUtil = new PhotoOrCropUtil(this); photoOrCropUtil.setPhotoOrCropListener(new PhotoOrCropUtil.PhotoOrCropListener() { @Override public void onSuccess(File file) { //file是没有压缩的图片,如果需要压缩就compressPic compressPic(file); } @Override public void onSuccess(List<File> files) { //files是没有压缩的图片列表,如果需要压缩就compressPics compressPics(files); } @Override public void onError(String error) { showToast(error); } }); } private void compressPics(List<File> files) { photoOrCropUtil.compressPic(files, new CompressUtils.CompressFinishCallBack<List<File>>() { @Override public void compressStart(String msg) { //压缩前 } @Override public void compressFinish(List<File> files) { //压缩完成 addImageViews(files); } @Override public void compressError(String msg) { showToast(msg); } }); } private void compressPic(File file) { photoOrCropUtil.compressPic(file, new CompressUtils.CompressFinishCallBack<File>() { @Override public void compressStart(String msg) { //压缩前 } @Override public void compressFinish(File files) { //压缩完成 addImageView(files); } @Override public void compressError(String msg) { showToast(msg); } }); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { photoOrCropUtil.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data); } @OnClick({R.id.camera, R.id.single_gallery, R.id.multi_gallery}) public void onClick(View view) { switch (view.getId()) { case R.id.camera: photoOrCropUtil.openCamera(false); break; case R.id.single_gallery: photoOrCropUtil.openGallerySingle(false); break; case R.id.multi_gallery: photoOrCropUtil.openGalleryMuti(9); break; } } private void addImageViews(List<File> files) { for (File f : files) { addImageView(f); } } private void addImageView(File file) { ImageView view = new ImageView(this); view.setLayoutParams(new LinearLayout.LayoutParams(200, 200)); view.setImageURI(Uri.fromFile(file)); imageLayout.addView(view); } private void showToast(String text) { Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show(); }}2、布局:activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <Button android:id="@+id/camera" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="拍照" /> <Button android:id="@+id/single_gallery" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_vertical_margin" android:text="单选相册" /> <Button android:id="@+id/multi_gallery" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_vertical_margin" android:text="多选相册" /> <HorizontalScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/image_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> </LinearLayout> </HorizontalScrollView></LinearLayout>
例子在此,估计很容易看懂吧,其实也没什么东西可说,感觉还是蛮简单的哦。
上面写的压缩图片,最好是写在选择图片完成或者处理完成之后,再进行压缩图片,要不然可能会出现很多缓存的压缩图片,虽然不大,不过还是要注意下,本文还是有点欠缺之处就是luban压缩图片生成之后的删除问题。
还有推荐一款好用的图片选择器 GalleryFinal,最近也是根据项目需要换成了这个。
GalleryFinal是一个图片选择(单选/多选)、拍照、编辑、裁剪及旋转为一身的图片选择器。使用方便,功能可自己配置,GalleryFinal还可以根据开发者的喜好来选择主题,当然也支持自定义主题。而且GalleryFinal自身并没有强制绑定某个ImageLoader,开发者可以根据自己项目给GalleryFinal配置图片加载器。GalleryFinal还放弃了startActivityForResult+onActivityResult来获取选择结果,而是采用事件回调的机制。(我觉得startActivityForResult+onActivityResult太麻烦了,同意的点个赞吧,呵呵~)。GalleryFinal经过三四个月的版本迭代,修复bug,优化需求,已经处于稳定的状态。
- Android 用MultiImageSelector实现单图/多图+压缩
- MultiImageSelector
- 多图选择器MultiImageSelector的使用
- [C#]用.NET框架实现ZIP单文件压缩
- 问题解决--实现仿微信多图选择的开源项目(MultiImageSelector)
- Android之多图片选择器MultiImageSelector库的使用(仿微信)
- galleryfinal 实现Android图片单选/多选、拍照、裁剪、压缩。视频选择和录制。
- Android中多图片选择器MultiImageSelector库的使用(仿微信)
- Android中多图片选择器MultiImageSelector库的使用2(仿微信)
- Android中多图片选择器PhotoPicker库的使用(仿微信,秒杀MultiImageSelector)
- Android 实现单选列表
- android listView实现单选
- Android 实现单选对话框
- Android ListView实现单选
- Android图片压缩解析与应用实现图片压缩缓存
- MultiImageSelector Github项目地址
- MultiImageSelector设置头像调用
- android系列:用RadioButton实现ABCD单选效果
- 步进电机使用总结——噪声与振动的抑制
- Service Broadcast简单音乐播放功能
- 深度学习GPU运算 cuda 百度云下载
- iOS
- android 之 service
- Android 用MultiImageSelector实现单图/多图+压缩
- Java线程同步阻塞, sleep(), suspend(), resume(), yield(), wait(), notify()
- CentOS7.3 安装 MySQL5.7.18 RPM Bundle
- VBO与PBO,DMA异步快速传递
- 【解题报告】Educational Codeforces Round 21
- python对excel文件进行操作
- 观察者模式
- 使用kernel-package编译内核imgage包
- android 之 Intent、broadcast