照片选择

GalleryFinal GitHub [使用方法]

相信做过照片选择的童鞋都会觉得比较痛苦, 由于手机Rom差异化很大, 导致使用原生的相机/相册可能出现很多兼容性问题. 比如在这几款手机上是正常的, 到了另外一个手机就会闪退. 还有部分手机照片被翻转, MIUI当年与众不同的Uri等等各种问题. 为了避免这些问题再继续折磨我们, 我选用了一个开源的第三方库GalleryFinal, 链接在最上方.

由于这个库目前也不是很成熟, 再加上作者也挺长时间没有更新, 所以这也是一个过渡方案. 最好的方案是抽时间自己造一个.

在这里不打算讲GalleryFinal, 只讲一个多选的例子. 首先GalleryFinal使用之前需要大量的配置, 因此将这些配置代码专门汇总到GalleryFinalConfigurator类, 避免在每一个项目里都要复制粘贴:

GalleryFinalConfigurator.config(context);

调用上述代码之后我们就可以直接使用GalleryFinal了. 现在有一个需求, 用户可以选任意0-9张图片, 并且可以删除已选的图片. 根据需求, 应选用GridView作为展示选择后的图片的载体. 那么作为GridView的item应该提供两个ImageView控件, 一个用于显示选中的图片, 一个用于删除.

基本上多选图片都包含有以下逻辑:

  • 初始情况下, 显示一个"添加照片"的按钮, 除非达到最大图片数, 不然该按钮一直显示在最后一个. 达到最大数则隐藏该按钮.

  • 选择图片后, 照片从最后一个开始按顺序显示.

  • 选择的图片永远不能大于最大数

  • 删除中间某一张图片后, 后面的图片依次往前挪.

  • 删除图片需要判断"添加照片"按钮是否是显示的, 不显示的情况下需要显示出来. 显示的情况下按钮也需要跟随照片往前挪移.

这些逻辑可以完全写在一个帮助类中, 以后我们使用的时候只需要设置最大数, 提供布局, 控制照相/相册弹窗的显示即可. 因此ChooseImageHelper诞生了. 首先看看ChooseImageHelper的构造函数与公共方法:

public ChooseImageHelper(int maxSize, GridView display, int itemLayoutResId, 
          OnOpenChooseDialogCallback callback);
public List<File> getChosenImages();
public void openGallery(int position);
public void openCamera(int position);

构造函数中, 需要传入一个最大数量, 比如上述需求是"用户可以选任意0-9张图片", 因此maxSize应该是9, 然后传入一个GridView控件用作展示. 接着需要传入一个布局Id, 作为GridView的布局, 需要注意的是此布局中需要包含两个ImageView, 一个id为image, 另一个为delete. 最后传入一个OnOpenChooseDialogCallback实现, ChooseImageHelper会在需要显示相机/相册选择框的时候调用onOpen方法, 并且将点击的position传递出来. 接着一旦我们监控到用户选择了相机或相册, 就可以调用openGallery(position)/openCamera(position)来使用GalleryFinal. 后续的事情我们就不用控制了. 最后如果需要将选择的图片上传至服务器, 则可以调用getChosenImages()方法获取File集合.

接下来看看具体代码:

代码很简单, 但是这样就足够完成多选功能了. 在onOpen回调中, window是一个PopupWindow对象, 并且在屏幕底部显示. 当然, 既然提供了回调(OnOpenChooseDialogCallback), 当然也会提供一份相应的Observable.

由于需要显示"添加照片"的按钮, 而GridView又是由Adapter控制, 如果没有选择照片的情况下, 理论上adapter的getCount应该为0, 但是这样就无法显示按钮. 因此需要额外添加一个实体类, 包含一个标志位 - 是否选择了图片. 如果true, 就显示图片, false就显示按钮. 只要adapter中默认包含一个标志位为false的实例即可.

接下来是Adapter的代码, Adapter使用了QuickAdapter. Adapter中也持有一个maxSize, 用于计算"添加照片"按钮是否显示. 关键则在onClick事件中有关删除的代码:

以上就是整个ChooseImageHelper的关键代码. 你以为这样就完了? 不是, 这里有个坑, 通俗点就是有个内存泄漏, 请看GalleryFinal类的部分代码:

可以看到, OnHanlderResultCallback内部类被复制给了GalleryFinal类的静态成员变量, 并且没有置空. 这意味着OnHanlderResultCallback会一直存在, 一旦OnHanlderResultCallback内直接或间接的引用了Context, 就会内存泄漏. 在上面的代码中, OnHanlderResultCallback内部引用了adapter, adapter内部又引用了context, 所以造成了内存泄漏.

Memory leak

解决办法很简单, 在适当的时候将mCallback置空. 但是想要加上上述代码, 只能将GalleryFinal代码down下来修改. 因此实现自己的相片选择理应提上日程.

Last updated

Was this helpful?