前言
最近项目中需要使用到图片选择器,网上搜索了一下,这方面的库有很多,最终是参考的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);
}
}
}
因为时间的原因,还没有实现可配置图片缓存相关,之后有时间会加上~大家在使用库的过程中,有任何问题,欢迎反馈哈~