听说你想做一个图片选择器

想必大家都见识过微信的图片选择器,我不得不承认,恩,确实做得不错啊!让我们来看看它的容貌。

听说你想做一个图片选择器_第1张图片
微信图片选择器.jpg

那么我是不是也能搞一个这样的图片选择器呐,答案当然是肯定的。下面先让各位见识下我做的图片选择器,和微信搞的比较一下。不比较一下,不比较一下我都不知道自己做的有多low。

听说你想做一个图片选择器_第2张图片
llf的图片选择器.jpg

眼尖的朋友可能发现了图片数量不一样,恩,的确不一样,其实我发现微信的图片选择器各个类别下图片加起来的数量跟所有图片的数量是对不上的,不过这都不是重点,我也不抠这些细节了。下面我来讲讲我这个图片选择器是怎么实现的。

  • 首先要获取本地图片数据,这步类似于查询数据库,是一个耗时操作。需要异步执行
  • 将这些图片数据显示出来,这一步要用到图片加载工具,我这边用的是Glide,当然还有其他的工具,不过这边我想说的就是最好做一下封装,原因大家应该都懂的。http://www.jianshu.com/p/e26130a93289 个人感觉这篇文章写的不错。
1.获取本地图片

这里用到一个类Loader(加载器),Loader是一个异步加载数据的类。为什么用这个类而不用AsyncTask或则Handler加Thread呐,下面是它的一些特性,看完就明白为什么用它了!

  • 提供异步加载数据的功能。
  • 监视它们的数据资源,并在这些资源发生变化时发送新的结果。
  • 当配置信息发生改变后重新被创建时,它会重连到上一次装载机的指示位置。而且,它们不需要重新查询数据。
  • 允许一个Activity或者Fragment连接到Activity或者Loader被重新创建之前的Loader,并且重新取出里面的result。
  • 如果result在Loader从Activity/Fragmentdisconnected之后才得到,那么它会被保存在cache中,并且在Activity/Fragemtneon被重新创建之后传送回来。
  • Loader可以监控他得数据源,在数据源数据变化后将新的数据deliver(传递)出来。
  • Loader处理了result相关的资源的allocation/disallocation(比如Cursors)。

第一步:建立loaderManager


getSupportLoaderManager().initLoader(int id,Bundle args,LoaderCallbacks callback);

三个参数分别是加载器的唯一Id,提供给加载器的参数,一个LoaderManager.loaderCallBacks实现。这样就初始化了一个Loader,但是真正的实现是在回调方法onCreateLoader()中。我们这边用到的是CursorLoader,代码如下:

private LoaderManager.LoaderCallbacks mLoaderCallback = new LoaderManager.LoaderCallbacks() {
@Override
public Loader onCreateLoader(int id, Bundle args) {
CursorLoader cursorLoader = new CursorLoader(getActivity(),
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION,
null, null, IMAGE_PROJECTION[2] + " DESC");
return cursorLoader;
}
return null;
}

    @Override
    public void onLoadFinished(Loader loader, Cursor data) {
   }

重点在于CursorLoader的新建,有六个参数,分别是上下文,要检索内容的URI,要返回的某一列元素的列表,声明要返回哪些行的过滤器,过滤器的值,如何对行进行排序,其实跟数据库查询语句一模一样。我这边的排序是根据时间倒序。查询完成之后会回调到onLoaderFinished方法中。


@Override
public void onLoadFinished(Loader loader, Cursor data) {
if (data != null) {
folderList.add(new Folder());
int count = data.getCount();
if (count > 0) {
List tempImageList = new ArrayList<>();
data.moveToFirst();
do {
String path = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[0]));
String name = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[1]));
long dateTime = data.getLong(data.getColumnIndexOrThrow(IMAGE_PROJECTION[2]));
Image image = new Image(path, name, dateTime);
if (!image.path.endsWith("gif"))
tempImageList.add(image);
if (!hasFolderGened) {
File imageFile = new File(path);
File folderFile = imageFile.getParentFile();
Folder folder = new Folder();
folder.name = folderFile.getName();
folder.path = folderFile.getAbsolutePath();
folder.cover = image;
if (!folderList.contains(folder)) {
List imageList = new ArrayList<>();
imageList.add(image);
folder.images = imageList;
folderList.add(folder);
} else {
Folder f = folderList.get(folderList.indexOf(folder));
f.images.add(image);
}
}
} while (data.moveToNext());
imageList.clear();
if (config.needCamera)
imageList.add(new Image());
imageList.addAll(tempImageList);
imageListAdapter.notifyDataSetChanged();
mFolderListAdapter.notifyDataSetChanged();
hasFolderGened = true;
}
}
}

这里有一个细节imageFile.getParentFile()得到图片所在的文件夹,用于左下角按文件筛选图片。
接下来都是UI上的操作了没什么难度。这时候的界面应该是这个样子的


听说你想做一个图片选择器_第3张图片
没有照相机.jpg

当你欣喜若狂之时,产品过来说怎么没有相机拍摄。早点怎么不说,只能加呗。代码大致是下面这样的。


Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (cameraIntent.resolveActivity(getActivity().getPackageManager()) != null) {
tempFile = new File(FileUtils.createRootPath(getActivity()) + "/" + System.currentTimeMillis() + ".jpg");
FileUtils.createFile(tempFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));
startActivityForResult(cameraIntent, REQUEST_CAMERA);
} else {
Toast.makeText(getActivity(), "打开相机失败", Toast.LENGTH_SHORT).show();
}

过了几天,产品又说这个界面上不需要图片多选,你这个图片怎么不能裁剪。。。我日你哥,你怎么这么多事。这时候就在想在改下去代码会越来越乱,判断会永无止境。同时这也不符合开闭原则。这时候就在想搞个配置文件的好处了。这样灵活了很多。

public class ImgSelConfig {
/**
* 是否需要裁剪
*/
public boolean needCrop;

/**
 * 是否多选
 */
public boolean multiSelect;

/**
 * 最多选择图片数
 */
public int maxNum = 9;

/**
 * 第一个item是否显示相机
 */
public boolean needCamera;
/**
 * 拍照存储路径
 */
public String filePath;
/**
 * 裁剪输出大小
 */
public int aspectX = 1;
public int aspectY = 1;
public int outputX = 500;
public int outputY = 500;

private ImgSelConfig(Builder builder){
    this.needCrop = builder.needCrop;
    this.multiSelect = builder.multiSelect;
    this.maxNum = builder.maxNum;
    this.needCamera = builder.needCamera;
    this.filePath = builder.filePath;
    this.aspectX = builder.aspectX;
    this.aspectY = builder.aspectY;
    this.outputX = builder.outputX;
    this.outputY = builder.outputY;
}
public static class Builder implements Serializable {

    private boolean needCrop = false;
    private boolean multiSelect = true;
    private int maxNum = 9;
    private boolean needCamera = true;
    private String filePath;

    private int aspectX = 1;
    private int aspectY = 1;
    private int outputX = 400;
    private int outputY = 400;

    public Builder() {
        if (FileUtils.isSdCardAvailable())
            filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Camera";
        else
            filePath = Environment.getRootDirectory().getAbsolutePath() + "/Camera";
        FileUtils.createDir(filePath);
    }

    public Builder needCrop(boolean needCrop) {
        this.needCrop = needCrop;
        return this;
    }

    public Builder multiSelect(boolean multiSelect) {
        this.multiSelect = multiSelect;
        return this;
    }

    public Builder maxNum(int maxNum) {
        this.maxNum = maxNum;
        return this;
    }

    public Builder needCamera(boolean needCamera) {
        this.needCamera = needCamera;
        return this;
    }

    private Builder filePath(String filePath) {
        this.filePath = filePath;
        return this;
    }

    public Builder cropSize(int aspectX, int aspectY, int outputX, int outputY) {
        this.aspectX = aspectX;
        this.aspectY = aspectY;
        this.outputX = outputX;
        this.outputY = outputY;
        return this;
    }

    public ImgSelConfig build() {
        return new ImgSelConfig(this);
    }
}

}

你可能感兴趣的:(听说你想做一个图片选择器)