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

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

微信图片选择器.jpg

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

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
<pre>
getSupportLoaderManager().initLoader(int id,Bundle args,LoaderCallbacks callback);</pre>
三个参数分别是加载器的唯一Id,提供给加载器的参数,一个LoaderManager.loaderCallBacks实现。这样就初始化了一个Loader,但是真正的实现是在回调方法onCreateLoader()中。我们这边用到的是CursorLoader,代码如下:
<pre>
private LoaderManager.LoaderCallbacks<Cursor> mLoaderCallback = new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> 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<Cursor> loader, Cursor data) {
   }

</pre>

重点在于CursorLoader的新建,有六个参数,分别是上下文,要检索内容的URI,要返回的某一列元素的列表,声明要返回哪些行的过滤器,过滤器的值,如何对行进行排序,其实跟数据库查询语句一模一样。我这边的排序是根据时间倒序。查询完成之后会回调到onLoaderFinished方法中。
<pre>
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data != null) {
folderList.add(new Folder());
int count = data.getCount();
if (count > 0) {
List<Image> 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<Image> 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;
}
}
}</pre>
这里有一个细节imageFile.getParentFile()得到图片所在的文件夹,用于左下角按文件筛选图片。
接下来都是UI上的操作了没什么难度。这时候的界面应该是这个样子的


没有照相机.jpg

当你欣喜若狂之时,产品过来说怎么没有相机拍摄。早点怎么不说,只能加呗。代码大致是下面这样的。
<pre>
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();
}
</pre>

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

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);
    }
}

}
</pre>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,478评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,825评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,482评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,726评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,633评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,018评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,168评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,320评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,264评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,288评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,995评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,587评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,909评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,284评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,862评论 2 339

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,263评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,561评论 18 139
  • 1 背景## 在Android中任何耗时的操作都不能放在UI主线程中,所以耗时的操作都需要使用异步实现。同样的,在...
    我是昵称阅读 1,207评论 0 3
  • 【漫步人生路】人的一生会遭遇无数次相逢,有些人,是你看过便忘了的风景。有些人,则在你的心里生根抽芽。那些无法诠释的...
    晴云秋月秋意浓阅读 1,266评论 2 2
  • “姥,我回来喽" “怎么才回来,天都黑了,天气早晚凉了赶紧回家把褂子穿上" “我不冷,俺姥爷呢,你什么时候回来啊,...
    关尘阅读 252评论 0 0