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,优化需求,已经处于稳定的状态。

阅读全文
0 0
原创粉丝点击