私人订制Android本地图片选择器

auxo2010 8年前
   <h3><strong>效果图</strong></h3>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a0c8cbeb2edc2b7d41893100e2dd96c5.png"></p>    <h3><strong>需求分析</strong></h3>    <ul>     <li>网格布局显示本地图片</li>     <li>支持图片多选</li>     <li>支持选中的图片预览</li>     <li>未选择图片时不可预览</li>     <li>由已选多图变为无图时可退出图片选择页面</li>     <li>图片已选达到上限后依然可以跳转图片选择页面</li>    </ul>    <h3><strong>第三方框架使用</strong></h3>    <p>史上最强的安卓图片选择器—— GalleryFinal ;</p>    <p><strong>实现目标</strong></p>    <p>应用并修改第三方框架GalleryFinal源码,实现效果图的样式。</p>    <h3><strong>具体实现</strong></h3>    <ul>     <li style="text-align:center"> <p style="text-align:left">导入GalleryFinal源码</p> <p style="text-align:left">从GalleryFinal的Github仓库中拷贝仓库地址,使用git指令cloneGalleryFinal源代码到本地:</p> <pre style="text-align:left">  <code class="language-java">git clone https://github.com/pengjianbo/GalleryFinal.git</code></pre> <p style="text-align:left">选择Android Studio菜单栏File->New->import Module...,导入GalleryFinal源代码到Android Studio:</p> </li>    </ul>    <p style="text-align:center"> </p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a533b7cf8843f16acf51a759d8c9a9ef.png"></p>    <p style="text-align:center">导入GalleryFinal源码</p>    <ul>     <li style="text-align:center"> <p style="text-align:left">写一个GridView</p> <p style="text-align:left">这里声明一下,选择图片的Activity是GalleryFinal自带的,所以我们这里要写的GridView是用来显示选中并返回的图片,这里的代码就不放出来了,效果图如下:</p> <img src="https://simg.open-open.com/show/2b3765703d02fb2612d5bacbc9202da1.png"> <p>未选择时</p> <img src="https://simg.open-open.com/show/ef9193219354bf5d419cd603b02a6ba0.png"> <p>选择图片时</p> <p style="text-align:left">本人实现的可支持最大图片数量是5,在图片选满的时候依然显示“+”,用以跳转图片选择页面选择其他图片。</p> </li>     <li> <p>初始化GalleryFinal配置</p> <p>为防止代码分开查看导致逻辑的混乱,将上述配置代码一齐贴上,下面的代码可放在跳转图片选择界面的按钮点击事件中。 <strong>这里强调一下</strong> :mThemeConfig = new ThemeConfig.Builder() .setTitleBarBgColor等方法传参是整型,但是其传入的是颜色值而非资源文件的id。</p> <p>代码注释较详细,其他不做过多讲解:</p> <pre>  <code class="language-java">// ------- 声明 -------  // 主题配置  private ThemeConfig mThemeConfig;  // 图片加载器  private cn.finalteam.galleryfinal.ImageLoader mGlidImgLoader;  // 滚动监听事件  private PauseOnScrollListener mPauseOnScrollListener;  // 功能配置  private FunctionConfig mFunctionConfig;  // 核心配置  private CoreConfig mCoreConfig;  // ------- 实现 -------  // 获取标题栏背景颜色  int colorTitleBarBg = ContextCompat.getColor(Activity.this, R.color.titleBarBgColor);  // 标题栏文字颜色  int colorTitleBarText = ContextCompat.getColor(Activity.this, R.color.titleBarTextColor);  // 浮动按钮常规颜色  int colorFabNormal = ContextCompat.getColor(Activity.this, R.color.color_ffaa2a);  // 浮动按钮点击颜色  int colorFabPressed = ContextCompat.getColor(Activity.this, R.color.color_e29428);  // 标题栏按钮颜色  int colorTitleBarIcon = ContextCompat.getColor(MainActivity.this, R.color.colorTitleBarIcon);  // 设置主题  mThemeConfig = new ThemeConfig.Builder()        .setTitleBarBgColor(colorTitleBarBg) // 设置标题栏背景颜色        .setTitleBarTextColor(colorTitleBarText)    // 设置标题栏文字颜色        .setFabNormalColor(colorFabNormal)  // 设置浮动按钮常规颜色        .setFabPressedColor(colorFabPressed)    // 设置浮动按钮点击颜色        .setCheckSelectedColor(colorFabNormal)  // 设置选中标记(对勾)的颜色和按钮的颜色相同        .setTitleBarIconColor(colorTitleBarIcon) // 设置标题栏按钮颜色        .setIconBack(R.drawable.ic_back) // 设置返回按钮        .build();  // 初始化图片加载器  mGlidImgLoader = new GlideImageLoader();  // 初始化监听事件  mPauseOnScrollListener = new GlidePauseOnScrollListener(false, true);  // 初始化功能配置  FunctionConfig.Builder funConBuilder = new FunctionConfig.Builder();  // 设置最多可选择5张照图片  funConBuilder.setMutiSelectMaxSize(5);  // 设置图片不可编辑  funConBuilder.setEnableEdit(false);  // 设置图片不可旋转  funConBuilder.setEnableRotate(false);  // 设置图片不可裁剪  funConBuilder.setEnableCrop(false);  // 设置不可通过照相选择照片  funConBuilder.setEnableCamera(false);  // 设置添加过滤集合,过滤掉之前选中的图片  //        funConBuilder.setFilter(mPhotoList);  // 不过滤图片,而是将之前选中的图片设置为选中状态  funConBuilder.setSelected(mPhotoList);  // 设置可预览  funConBuilder.setEnablePreview(true);  // 功能配置  mFunctionConfig = funConBuilder.build();  // 初始化核心配置  mCoreConfig = new CoreConfig.Builder(ReportActivity.this, mGlidImgLoader, mThemeConfig)        .setFunctionConfig(mFunctionConfig) // 添加功能配置        .setPauseOnScrollListener(mPauseOnScrollListener) // 滑动停止加载事件        .setNoAnimcation(true) // 无特效动画        .build();  // 实例化  GalleryFinalGalleryFinal.init(mCoreConfig);  // 多图片选择打开相册  GalleryFinal.openGalleryMuti(Constants.REQUEST_CODE_GALLERY, mFunctionConfig,        mOnHandlerResultCallback);  // 初始化图片加载器  initImageLoader(ReportActivity.this);</code></pre> <pre>  <code class="language-java">/**  * 初始化图片加载器  *  * @param context  */  private void initImageLoader(Context context) {    // 图片加载器配置    ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(this);    // 设置线程优先级    config.threadPriority(Thread.NORM_PRIORITY - 2);    // 禁止内存缓存    config.denyCacheImageMultipleSizesInMemory();    // 设置磁盘缓存文件名生成器    config.diskCacheFileNameGenerator(new Md5FileNameGenerator());    // 设置磁盘缓存大小    config.diskCacheSize(20 * 1024 * 1024);    // 设置任务进程执行顺序:先进后出    config.tasksProcessingOrder(QueueProcessingType.LIFO);    // 调试使用,若是发布版,需要移除代码    config.writeDebugLogs();    // 初始化图片加载器    ImageLoader.getInstance().init(config.build());  }</code></pre>      <ul>       <li>通过代码设置图片选择器的标题栏背景颜色,标题文本颜色,浮动按钮颜色;</li>       <li>通过监听事件,达到滚动时不加载图片,停下来时加载图片,实现优化;</li>       <li>初始化功能配置;</li>      </ul> </li>     <li> <p>选择图片返回的回调实现</p> <pre>  <code class="language-java">/**  * 回调处理  */  private GalleryFinal.OnHanlderResultCallback mOnHandlerResultCallback = new GalleryFinal.OnHanlderResultCallback() {    @Override    public void onHanlderSuccess(int reqeustCode, List<PhotoInfo> resultList) {        // 清除原来列表中的图片        mPhotoList.clear();        // 返回图片列表        mPhotoList.addAll(resultList);        // 刷新页面        mPhotoAdapter.notifyDataSetChanged();    }    @Override    public void onHanlderFailure(int requestCode, String errorMsg) {        // 错误提示        Toast.makeText(Activity.this, errorMsg, Toast.LENGTH_SHORT).show();    }  };</code></pre> </li>    </ul>    <p>基于上述代码,可得到效果图如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4ae05200ca810761db44bb2f8f14088b.png"></p>    <p style="text-align:center">初步效果图</p>    <h3><strong>修改源码</strong></h3>    <p>通过运行调试,发现框架中有些功能与需求不一致,因此我产生了修改源码的想法,总结需要更改的原功能点如下:</p>    <p style="text-align:center">选择完达到上限数量的图片后,无法重新回到图片选择页面<br> <img src="https://simg.open-open.com/show/15cc940ef439444417607c0c2d355544.png"></p>    <p>选择图片数量达到上限时无法进入图片选择页面</p>    <ul>     <li> <p>无图片选择时,无法点击浮动按钮进行返回</p> </li>    </ul>    <p style="text-align:center"><br> <img src="https://simg.open-open.com/show/35860b28a963ebd605f29cf72c21f378.png"></p>    <p style="text-align:center">无图片时无法返回</p>    <ul>     <li>无图片选择时,预览按钮依然存在</li>    </ul>    <p style="text-align:center"><br> <img src="https://simg.open-open.com/show/c9d19f6e27f8e1852b13ce9a90aa1690.png"></p>    <p>无图片选择时预览按钮依然存在并可以点击</p>    <p>针对以上需要修改的功能,源码修改如下:</p>    <p style="text-align:center">选择图片到达上限依然可以返回图片选择页面</p>    <p style="text-align:center">因图片选择页面的跳转在openGalleryMuti方法里实现:</p>    <pre style="text-align:center">  <code class="language-java">// 多图片选择打开相册  GalleryFinal.openGalleryMuti(Constants.REQUEST_CODE_GALLERY, mFunctionConfig,        mOnHandlerResultCallback);</code></pre>    <p style="text-align:center">所以我们来看看openGalleryMuti方法的源码:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/fe47625a34d1ba588ddd910d88d8fecd.png"></p>    <p style="text-align:center">openGalleryMuti方法源码</p>    <p style="text-align:center">可以发现,源码中有这么一个判断逻辑:</p>    <pre style="text-align:center">  <code class="language-java">if (config.getSelectedList() != null && config.getSelectedList().size() > config.getMaxSize()) {    if(callback != null){        callback.onHanlderFailure(requestCode, mCoreConfig.getContext().getString(R.string.select_max_tips));    }    return;  }</code></pre>    <p style="text-align:center">其作用是当选中的图片数量超过最大值时,返回打开本地图片选择器失败的提示信息。之前我们提到,需求中我们实际多显示了一张图片:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/23ac3323e23e1205dd489200af078ef8.png"></p>    <p style="text-align:center">“添加”图片</p>    <p>且在配置的时候传入的是添加了一张图片以后的图片列表:</p>    <pre>  <code class="language-java">// 不过滤图片,而是将之前选中的图片设置为选中状态  funConBuilder.setSelected(mPhotoList);</code></pre>    <p>所以会导致界面无法跳转,我们有三个策略:</p>    <p>1.将多添加的图片放到adapter里面处理,adapter外部保持选中图片数量与选择页面传入图片的数量一致</p>    <p>2.setSelected传入图片列表之前将mPhotoList移除多出的图片</p>    <p>3.注释掉源码中对图片数量上限的判断</p>    <p>无图选择时,点击浮动按钮可以返回</p>    <p>可能有人不解,为何不点击标题栏的返回按钮返回而要点击浮动按钮返回?其原因是,若之前我选择好图片,但是想想,现在我不想要选择的图片了,这时候我们想把图片清空掉,这时候需要点击浮动按钮,来更新选中图片的列表。</p>    <p>想到这是浮动按钮的点击事件,所以我们到源码的GallerySelectActivity中浮动按钮的事件回调方法中:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f490db2e53e69f900861da3cfad02d62.png"></p>    <p>浮动按钮点击事件</p>    <p>这段代码仅仅在选中图片的数量大于0的时候才执行操作,所以我们添加一个条件,修改后的代码如下:</p>    <pre>  <code class="language-java">if (mSelectPhotoList.size() > 0) {    if (!GalleryFinal.getFunctionConfig().isEditPhoto()) {        resultData(mSelectPhotoList);    } else {        toPhotoEdit();    }  } else {    // 添加的代码,使未选中图片时也可返回    resultData(mSelectPhotoList);  }</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6c3c160df00df09642edf1b579b63561.png"></p>    <p>mSelectPhotoList初始化</p>    <p>考虑到选中图片的列表在声明时已经初始化,所以不用担心图片返回的回调事件传入空指针对象。</p>    <ul>     <li> <p>未选择图片时不显示预览按钮</p> <p>通过布局的id—— <strong>iv_preview</strong> 在PhotoSelectActivity中查找,在refreshSelectCount方法里找到了对预览按钮可见性的设置:</p> <img src="https://simg.open-open.com/show/883e0bcd08832fd52908b9ad4b8d7d1f.png"> <p>refreshSelectCount方法源码</p> <p>从源码中可以看到,预览按钮的可见性判断逻辑仅仅与isEnablePreview有关,而没有和选中的图片数量进行关联,所以我们修改代码如下:</p> <pre>  <code class="language-java">public void refreshSelectCount() {    mTvChooseCount.setText(getString(R.string.selected, mSelectPhotoList.size(), GalleryFinal.getFunctionConfig().getMaxSize()));    if (mSelectPhotoList.size() > 0 && GalleryFinal.getFunctionConfig().isMutiSelect()) {        mIvClear.setVisibility(View.VISIBLE);        if (GalleryFinal.getFunctionConfig().isEnablePreview()) {            mIvPreView.setVisibility(View.VISIBLE);        }    } else {        mIvClear.setVisibility(View.GONE);        mIvPreView.setVisibility(View.GONE);    }  }</code></pre> <p>当选中图片列表大小为0的时候,隐藏预览按钮;大于0的时候再根据isEnablePreview()来判断是否显示预览按钮。</p> </li>    </ul>    <h3><strong>修改布局和代码逻辑</strong></h3>    <p>布局和代码逻辑的修改,其思路与上一节 <strong>修改源码</strong> 一样,因需求的效果图功能与GalleryFinal的功能基本一致,逻辑上并不需要做很多的修改,而布局的修改仅涉及到ImageButton变成Button,ImageView变成TextView以及控件位置的调整,在关联控件上和点击事件根据id来判断事件处理上做相应修改即可,在此不做赘述。</p>    <h3><strong>总结</strong></h3>    <p>使用GalleryFinal订制属于自己的图片选择器并不难,只需要循着需求的功能点,按照代码的逻辑一点点追踪源码并进行修改订制即可。诚恳地说,GalleryFinal框架的可移植性确实很强,在此推荐大家了解一下!</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/fd5ebfc4725e</p>    <p> </p>