一款好用的图片选择器

作者: 宇是我 | 来源:发表于2016-10-26 20:03 被阅读1518次

    前言

    最近项目中需要使用到图片选择器,网上搜索了一下,这方面的库有很多,最终是参考的ImageSelector的源代码实现。在使用的过程中,按照交互需求,我新增了一下功能,并修复了几个bug。最后我自己做了一些封装,采用Builder的设计模式设计实现了一个更加通用的库。在此做个记录,具体实现效果如下所示:

    代码地址:https://github.com/yushiwo/Universal-Image-Selector

    模块设计

    具体代码的逻辑实现,我就不在这边讲了,大家有兴趣可以看下源码~
    在对模块结构设计过程中,我参考了Universal-Image-Loader的设计思想。对外暴露ImageSelector.java和ImageSelectorConfigration.java类,用户通过ImageSelectorConfigration.java配置组件相关功能和样式;然后通过ImageSelector.java获取configration初始化组件以及启动组件的相关界面。

    ImageSelector

    /**
     * @author hzzhengrui
     * @Date 16/10/20
     * @Description
     */
    public class ImageSelector {
    
        private static final String TAG = ImageSelector.class.getSimpleName();
    
        private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageSelector which had already been initialized before. " + "To re-init ImageSelector with new configuration call ImageSelector.destroy() at first.";
        private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageSelector configuration can not be initialized with null";
        private static final String ERROR_NOT_INIT = "ImageSelector must be init with configuration before using";
    
        private static ImageSelector sInstance;
    
        private ImageSelectorConfiguration configuration;
    
        public static ImageSelector getInstance(){
            if (sInstance == null) {
                synchronized (ImageSelector.class) {
                    if (sInstance == null) {
                        sInstance = new ImageSelector();
                    }
                }
            }
            return sInstance;
        }
    
        // TODO: 16/10/20 实现config设置
        public void init(ImageSelectorConfiguration configuration){
            if(configuration == null){
                throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
            }
    
            if(this.configuration == null){
                this.configuration = configuration;
                ImageSelectorProxy.getInstance().setConfiguration(configuration);
            }else {
                Log.w(TAG, WARNING_RE_INIT_CONFIG);
            }
        }
    
        public boolean isInited() {
            return configuration != null;
        }
    
        private void checkConfiguration() {
            if (configuration == null) {
                throw new IllegalStateException(ERROR_NOT_INIT);
            }
        }
    
        public void destroy() {
            if (configuration != null) {
                configuration = null;
            }
        }
    
        /**
         * 开启图片选择页面
         * @param activity
         * @param imageList
         */
        public void launchSelector(Activity activity, ArrayList<String> imageList){
            checkConfiguration();
            ImageSelectorActivity.start(activity, imageList);
        }
    
        /**
         * 开启图片可删除选中图片的预览界面
         * @param activity
         * @param imageList
         * @param position
         */
        public void launchDeletePreview(Activity activity, ArrayList<String> imageList, int position){
            checkConfiguration();
            ImagePreviewActivity.startDeletePreview(activity, imageList, position);
        }
    
    }
    
    

    ImageSelectorConfiguration

    /**
    * @author hzzhengrui
    * @Date 16/10/20
    * @Description
    */
    public class ImageSelectorConfiguration {
    
       /** 最多可以选择图片的数目 */
       public int maxSelectNum = -1;
       /** 图片选择界面的列数 */
       public int spanCount = -1;
    
       public int selectMode = -1;
       public boolean isShowCamera = true;
       public boolean isEnablePreview = true;
    
       public boolean isEnableCrop = false;
    
       public Drawable imageOnLoading = null;
       public int imageResOnLoading = 0;
    
       public Drawable imageOnError = null;
       public int imageResOnError = 0;
    
       int titleBarColor = -1;
       int statusBarColor = -1;
       float titleHeight = -1;
    
    
       public ImageSelectorConfiguration(final Builder builder) {
           maxSelectNum = builder.maxSelectNum;
           spanCount = builder.spanCount;
           selectMode = builder.selectMode;
           isShowCamera = builder.isShowCamera;
           isEnablePreview = builder.isEnablePreview;
           isEnableCrop = builder.isEnableCrop;
           imageOnLoading = builder.imageOnLoading;
           imageResOnLoading = builder.imageResOnLoading;
           imageOnError = builder.imageOnError;
           imageResOnError = builder.imageResOnError;
           titleBarColor = builder.titleBarColor;
           statusBarColor = builder.statusBarColor;
           titleHeight = builder.titleHeight;
       }
    
       /**
        * 生成默认的图片选择器配置
        * @param context
        * @return
        */
       public static ImageSelectorConfiguration createDefault(Context context){
           return new Builder(context).build();
       }
    
       public static class Builder{
    
           public static final int DEFAULT_MAX_SELECT_NUM = 9;
           public static final int DEFAULT_SPAN_COUNT = 4;
           public static final int DEFAULT_SELECT_MODE = ImageSelectorConstant.MODE_MULTIPLE;
    
    
           private Context context;
    
           /** 最多可以选择图片的数目 */
           private int maxSelectNum = DEFAULT_MAX_SELECT_NUM;
           /** 图片选择界面的列数 */
           private int spanCount = DEFAULT_SPAN_COUNT;
    
           private int selectMode = DEFAULT_SELECT_MODE;
           private boolean isShowCamera = true;
           private boolean isEnablePreview = true;
           private boolean isEnableCrop = false;
    
           private Drawable imageOnLoading = null;
           private int imageResOnLoading = 0;
    
           private Drawable imageOnError = null;
           private int imageResOnError = 0;
    
           private int titleBarColor = -1;
           private int statusBarColor = -1;
           private float titleHeight = -1;
    
           public Builder(Context context) {
               this.context = context.getApplicationContext();
           }
    
           /**
            * 设置做多可选图片的数目
            * @param maxSelectNum
            * @return
            */
           public Builder setMaxSelectNum(int maxSelectNum){
               this.maxSelectNum = maxSelectNum;
               return this;
           }
    
           /**
            * 设置图片选择页面展示的列数
            * @param spanCount
            * @return
            */
           public Builder setSpanCount(int spanCount){
               this.spanCount = spanCount;
               return this;
           }
    
           /**
            * 设置选择模式,单选或者多选
            * @param selectMode
            * @return
            */
           public Builder setSelectMode(int selectMode) {
               this.selectMode = selectMode;
               return this;
           }
    
           /**
            * 设置是否可裁剪
            * @param enableCrop
            * @return
            */
           public Builder setEnableCrop(boolean enableCrop) {
               isEnableCrop = enableCrop;
               return this;
           }
    
           /**
            * 设置是否支持选择时候预览
            * @param enablePreview
            * @return
            */
           public Builder setEnablePreview(boolean enablePreview) {
               isEnablePreview = enablePreview;
               return this;
           }
    
           /**
            * 设置是否显示拍照按钮
            * @param showCamera
            * @return
            */
           public Builder setShowCamera(boolean showCamera) {
               isShowCamera = showCamera;
               return this;
           }
    
           /**
            * 设置图片加载时候的默认图
            * @param
            * @return
            */
           public Builder setImageOnLoading(Drawable image) {
               this.imageOnLoading = image;
               return this;
           }
    
           public Builder setImageOnLoading(int imageRes){
               this.imageResOnLoading = imageRes;
               return this;
           }
    
           public Builder setImageOnError(Drawable image) {
               this.imageOnError = image;
               return this;
           }
    
           public Builder setImageOnError(int imageRes){
               this.imageResOnError = imageRes;
               return this;
           }
    
           /**
            * 设置选择器titlebar的颜色
            * @param colorRes
            * @return
            */
           public Builder setTitleBarColor(int colorRes){
               this.titleBarColor = colorRes;
               return this;
           }
    
           /**
            * 设置选择器statusbar的颜色
            * @param colorRes
            * @return
            */
           public Builder setStatusBarColor(int colorRes){
               this.statusBarColor = colorRes;
               return this;
           }
    
           public Builder setTitleHeight(float titleHeight){
               this.titleHeight = titleHeight;
               return this;
           }
    
           public ImageSelectorConfiguration build(){
               initEmptyFieldsWithDefaultValues();
               return new ImageSelectorConfiguration(this);
           }
    
           private void initEmptyFieldsWithDefaultValues() {
               if(maxSelectNum <= 0){
                   maxSelectNum = DEFAULT_MAX_SELECT_NUM;
               }
               if(spanCount <= 0){
                   spanCount = DEFAULT_SPAN_COUNT;
               }
               if(selectMode <= 0){
                   selectMode = DEFAULT_SELECT_MODE;
               }
    
               if(imageResOnLoading == 0 && imageOnLoading == null){
                   imageResOnLoading = R.drawable.uis_ic_placeholder;
               }
    
               if(imageResOnError == 0 && imageOnError == null){
                   imageResOnError = R.drawable.uis_ic_placeholder;
               }
    
               if(titleBarColor == -1){
                   titleBarColor = R.color.uis_black;
               }
    
               if(statusBarColor == -1){
                   statusBarColor = R.color.uis_black;
               }
    
               if(titleHeight <= 0){
                   titleHeight = 48;
               }
           }
       }
    
    }
    
    

    在组件内部,通过一个代理类ImageSelectorProxy.java,统一处理configration的相关配置

    /**
    * @author hzzhengrui
    * @Date 16/10/24
    * @Description
    */
    public class ImageSelectorProxy implements IProxy{
    
       private static final String ERROR_NOT_INIT = "ImageSelector must be init with configuration before using";
    
       private static ImageSelectorProxy sInstance;
    
       ImageSelectorConfiguration configuration;
    
       public static ImageSelectorProxy getInstance(){
           if (sInstance == null) {
               synchronized (ImageSelector.class) {
                   if (sInstance == null) {
                       sInstance = new ImageSelectorProxy();
                   }
               }
           }
           return sInstance;
       }
    
       /**
        * 设置图片选择器的配置
        * @param configuration
        */
       public void setConfiguration(ImageSelectorConfiguration configuration){
           this.configuration = configuration;
       }
    
       private void checkConfiguration() {
           if (configuration == null) {
               throw new IllegalStateException(ERROR_NOT_INIT);
           }
       }
    
       @Override
       public int getMaxSelectNum() {
           checkConfiguration();
           return configuration.maxSelectNum;
       }
    
       @Override
       public int getSpanCount() {
           checkConfiguration();
           return configuration.spanCount;
       }
    
       @Override
       public int getSelectMode() {
           checkConfiguration();
           return configuration.selectMode;
       }
    
       @Override
       public boolean isEnablePreview() {
           checkConfiguration();
           return configuration.isEnablePreview;
       }
    
       @Override
       public boolean isShowCamera() {
           checkConfiguration();
           return configuration.isShowCamera;
       }
    
       @Override
       public boolean isEnableCorp() {
           checkConfiguration();
           return configuration.isEnableCrop;
       }
    
       @Override
       public Drawable getImageOnLoading(Resources res) {
           checkConfiguration();
           return configuration.imageResOnLoading != 0 ? res.getDrawable(configuration.imageResOnLoading) : configuration.imageOnLoading;
       }
    
       @Override
       public Drawable getImageOnError(Resources res) {
           checkConfiguration();
           return configuration.imageResOnError != 0 ? res.getDrawable(configuration.imageResOnError) : configuration.imageOnError;
       }
    
       @Override
       public int getTitleBarColor(Resources res) {
           checkConfiguration();
           return res.getColor(configuration.titleBarColor);
       }
    
       @Override
       public int getStatusBarColor(Resources res) {
           checkConfiguration();
           return res.getColor(configuration.statusBarColor);
       }
    
       @Override
       public float getTitleHeight() {
           checkConfiguration();
           return configuration.titleHeight;
       }
    }
    

    使用

    • build.gradle配置
    compile 'com.netease.imageSelector:android-imageselector-lib:1.0.1'
    
    • AndroidManifest配置
        // 设置权限
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.INTERNET"/>
        
        // 注册activity
        <activity
            android:name="com.netease.imageSelector.view.ImageSelectorActivity"
            android:theme="@style/Theme.AppCompat.NoActionBar" />
        <activity
            android:name="com.netease.imageSelector.view.ImagePreviewActivity"
            android:theme="@style/Theme.AppCompat.NoActionBar" />
        <activity
            android:name="com.netease.imageSelector.view.ImageCropActivity"
            android:theme="@style/Theme.AppCompat.NoActionBar" />
    
    • 初始化
      在需要使用此组件的Activity的onCreate方法中,或者在自定义Application的onCreate方法中初始化。
    public class MyApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            // 获取默认配置
            ImageSelectorConfiguration configuration = ImageSelectorConfiguration.createDefault(this);
    
              // 自定义图片选择器
    //        ImageSelectorConfiguration configuration = new ImageSelectorConfiguration.Builder(this)
    //                .setMaxSelectNum(9)
    //                .setSpanCount(4)
    //                .setSelectMode(ImageSelectorConstant.MODE_MULTIPLE)
    //                .setTitleHeight(48)
    //                .build();
              
            ImageSelector.getInstance().init(configuration);
        }
    }
    
    • 启动组件

      • 开启图片选择界面
      /**
       * 开启图片选择页面
       * @param activity
       * @param imageList
       */
      public void launchSelector(Activity activity, ArrayList<String> imageList)
      
      • 开启图片预览界面(带删除功能)
      /**
       * 开启图片可删除选中图片的预览界面
       * @param activity
       * @param imageList
       * @param position
       */
      public void launchDeletePreview(Activity activity, ArrayList<String> imageList, int position)  ImagePreviewActivity.startDeletePreview(activity, imageList, position)
      
    • 处理回调结果
      组件的界面,都是通过startActivityForResult的方式启动,所以,我们只需在自己调起组件的界面,处理返回结果即可。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data == null) {
            return;
        }
        // 接收图片选择器返回结果,更新所选图片集合
        if (requestCode == REQUEST_PREVIEW || requestCode == REQUEST_IMAGE) {
            ArrayList<String> newFiles = data.getStringArrayListExtra(OUTPUT_LIST);
            if (newFiles != null) {
                updateUI(newFiles);
            }
        }
    }
    

    因为时间的原因,还没有实现可配置图片缓存相关,之后有时间会加上~大家在使用库的过程中,有任何问题,欢迎反馈哈~

    相关文章

      网友评论

      • 97e1a7a5b043:正需要这个呢,谢谢
        宇是我:@微微的风的袁 不客气,使用过程中有什么问题欢迎提建议哈

      本文标题:一款好用的图片选择器

      本文链接:https://www.haomeiwen.com/subject/fvcjuttx.html