Android MVP+LoaderManager+CursorLoader实现图片搜索
2018-01-11 09:20
351 查看
版权声明:本文为博主原创文章,未经博主允许不得转载
系列教程:Android开发之从零开始系列
源码:AnliaLee/PhotoFactory,欢迎star
大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论
首先在Gradle中导入PhotoFactory库
配置权限(动态权限的配置这里就不赘述了)
在Activity中调用photoFactory.FactorySearch方法,完成相关配置后在回调中获取查询到的数据
onFinish返回给我们的list即为查询到的gif图片集合,我们可以通过之前配置的数据映射参数从map中拿出数据
打印数据看看
结合Glide图片加载库可以实现获取gif图片(仅显示gif格式的图片)的功能,效果如下
那么怎么定位图像数据呢?查阅资料后我们知道:
Android的多媒体文件主要存储在 /data/data/com.android.providers.media/databases 目录下,该目录下有两个db文件,
* 内部存储数据库文件:internal.db
* 存储卡数据库:external-XXXX.db
媒体文件的操作主要是围绕着这两个数据库来进行。这两个数据库的结构是完全一模一样的。这两个数据库包含的表:
album_art 、audio 、search 、album_info 、audio_genres、 searchhelpertitle、albums、 audio_genres_map、 thumbnails、
android_metadata、 audio_meta、 video、artist_info 、audio_playlists 、videothumbnails、artists 、audio_playlists_map、
artists_albums_map 、images
我们要找的就是images表中的数据,我们设置好查询内容和条件后就可以用CursorLoader去查数据了,创建Model层的执行类SearchPhotoModelImpl
整个图片搜索的实现过程就是这样了,至于PhotoFactory是怎样封装这个过程的大家可以去看下源码,代码不难,没有太多层的回调,并且关键的地方我都给了详细的注释,相信大家都能看懂。有啥疑问或建议欢迎留言评论,感激不尽。如果觉得写得还不错麻烦点个赞,你们的支持是我最大的动力~
系列教程:Android开发之从零开始系列
源码:AnliaLee/PhotoFactory,欢迎star
大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论
前言
之前写了篇Android项目实践——三行代码解决照片选择与压缩,我们利用封装好的PhotoFactory简化了从系统相册获取照片的操作,但要想筛选出指定的图片原有的功能就不够用了,于是我们继续开发和完善PhotoFactory,将简化操作进行到底。本次我们将使用LoaderManager+CursorLoader机制结合MVP设计模式实现图片搜索的功能新功能使用示例
在讲解功能的实现过程之前,先简单介绍一下如何使用。更新后的PhotoFactory可以根据图片的路径、名称或图片格式等条件搜索图片,执行搜索后返回符合条件图片的list。这里我们以筛选出手机本地所有gif图片为例:首先在Gradle中导入PhotoFactory库
repositories { ... maven { url 'https://jitpack.io' } } dependencies { compile 'com.github.AnliaLee:PhotoFactory:1.0.1' }
配置权限(动态权限的配置这里就不赘述了)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
在Activity中调用photoFactory.FactorySearch方法,完成相关配置后在回调中获取查询到的数据
//加载数据的映射(MediaStore.Images.Media.DATA等) String[] projection = new String[]{ MediaStore.Images.Media.DATA,//图片路径 MediaStore.Images.Media.DISPLAY_NAME,//图片文件名,包括后缀名 MediaStore.Images.Media.TITLE//图片文件名,不包含后缀 }; photoFactory = new PhotoFactory(this,this); photoFactory.FactorySearch(getSupportLoaderManager(),getApplicationContext(),projection) .setSelectionByFormat(new String[]{".gif"})//设置查询条件(通过图片格式查找,非必选) //.setSelection(new String[]{"图片收藏","WeiXin"}) (或模糊匹配搜索指定图片,非必选) .setLoadingEvent(new InterfaceManager.LoadingCallBack() {//设置异步加载时loading操作(非必选) @Override public void showLoading() { myProgressDialog.show(); } @Override public void hideLoading() { if(myProgressDialog.isShowing()){ myProgressDialog.dismiss(); } } }) .setErrorEvent(new InterfaceManager.ErrorCallBack() {//设置搜索出错时的操作(非必选) @Override public void dealError(String s) { Toast.makeText(SearchGifActivity.this, s, Toast.LENGTH_SHORT).show(); } }) .execute(new InterfaceManager.SearchDataCallBack() {//执行搜索并获取回调数据 @Override public void onFinish(final List<Map<String, Object>> list) { searchGifAdapter = new SearchGifAdapter(SearchGifActivity.this,list); recyclerView.setAdapter(searchGifAdapter); } });
onFinish返回给我们的list即为查询到的gif图片集合,我们可以通过之前配置的数据映射参数从map中拿出数据
list.get(position).get(MediaStore.Images.Media.DATA)
打印数据看看
结合Glide图片加载库可以实现获取gif图片(仅显示gif格式的图片)的功能,效果如下
MVP+LoaderManager+CursorLoader实现图片搜索
查询本地图片数据是一个异步获取数据的过程,因此我们不妨使用MVP设计模式将数据获取和数据展示分离开来(有关MVP设计模式的知识大家可以查阅相关资料进行了解,就不在这展开了)。为了让Model,View和Presenter三者之间可以相互引用并回调数据,同时保留后续扩展的可能性,我们定义相关接口供他们继承public interface InterfaceManager { /** * MVP模式接口 */ interface Model { void getData(Map<String, Object> map, ModelDataCallBack callBack); } interface View { void onFinish(List<Map<String, Object>> list); } interface Presenter{ void getData(Map<String, Object> map); } /** * model数据回调 */ interface Mo 4000 delDataCallBack { void getListDataSuccess(List<Map<String, Object>> list); void getDataFailed(String message); } /** * 搜索数据回调 */ interface SearchDataCallBack extends View{ @Override void onFinish(List<Map<String, Object>> list); } /** * 加载中回调 */ interface LoadingCallBack{ void showLoading(); void hideLoading(); } /** * 错误回调 */ interface ErrorCallBack{ void dealError(String message); } }
Presenter层的实现
我们先来看看Presenter层是怎么写的。Presenter作为Model和View的中间件,负责在两者之间传递数据,实现数据获取和展示的分离。我们创建Presenter接口的执行类SearchPhotoPresenterImpl,通过初始化传入Model和View的引用,在getData方法中先让Model去获取数据,等Model将数据回调后再执行View的方法将数据传回给用户,这样用户就可以开始处理数据了public class SearchPhotoPresenterImpl implements InterfaceManager.Presenter{ //省略部分代码... InterfaceManager.Model model;//定义Model层引用 //下面三个属于View层 InterfaceManager.View view; InterfaceManager.LoadingCallBack loadingCallBack; InterfaceManager.ErrorCallBack errorCallBack; private Handler mHandler = new Handler(); @Override public void getData(Map<String, Object> map) { mHandler.post(new Runnable() { @Override public void run() { if(loadingCallBack !=null){ loadingCallBack.showLoading(); } } }); model.getData(map, new InterfaceManager.ModelDataCallBack() {//让Model去获取数据 @Override public void getListDataSuccess(final List<Map<String, Object>> list) { //Model将数据回调后让View执行数据处理的操作 mHandler.post(new Runnable() { @Override public void run() { view.onFinish(list);//View层的方法 if(loadingCallBack !=null){ loadingCallBack.hideLoading(); } } }); } @Override public void getDataFailed(final String message) { mHandler.post(new Runnable() { @Override public void run() { if(errorCallBack !=null){ errorCallBack.dealError(message); } if(loadingCallBack !=null){ loadingCallBack.hideLoading(); } } }); } }); } }
Model层的实现
Model层负责异步查询数据,我们不需要自己写异步的逻辑,Android官方提供了LoaderManager+CursorLoader机制用来异步查找本地文件,我们只需要实现LoaderManager.LoaderCallbacks接口,并重写其内部相应方法// 在初始化Loader时回调,在这个方法中实例化CursorLoader public Loader<Cursor> onCreateLoader(int id, Bundle args); // 数据查询完毕后会回调这个方法,我们就在这将数据保存至list中并传给Presenter层 public void onLoadFinished(Loader<Cursor> loader, Cursor data); // 这个方法在重启Loader时才会调用,一般不需要重写 public void onLoaderReset(Loader<Cursor> loader);
那么怎么定位图像数据呢?查阅资料后我们知道:
Android的多媒体文件主要存储在 /data/data/com.android.providers.media/databases 目录下,该目录下有两个db文件,
* 内部存储数据库文件:internal.db
* 存储卡数据库:external-XXXX.db
媒体文件的操作主要是围绕着这两个数据库来进行。这两个数据库的结构是完全一模一样的。这两个数据库包含的表:
album_art 、audio 、search 、album_info 、audio_genres、 searchhelpertitle、albums、 audio_genres_map、 thumbnails、
android_metadata、 audio_meta、 video、artist_info 、audio_playlists 、videothumbnails、artists 、audio_playlists_map、
artists_albums_map 、images
我们要找的就是images表中的数据,我们设置好查询内容和条件后就可以用CursorLoader去查数据了,创建Model层的执行类SearchPhotoModelImpl
public class SearchPhotoModelImpl implements InterfaceManager.Model { /** * Loader的唯一ID号 */ private final static int IMAGE_LOADER_ID = 1000; @Override public void getData(Map<String, Object> map, final InterfaceManager.ModelDataCallBack callBack) { LoaderManager loaderManager = (LoaderManager) map.get("lm"); final Context applicationContext = (Context) map.get("ac"); final boolean isQueryByFormat = (boolean) map.get("isQueryByFormat");//是否只通过图片格式查询 final String[] selections = (String[]) map.get("selections");//查询条件 final String [] projection = (String[]) map.get("projection");//内容映射 //初始化指定id的Loader loaderManager.initLoader(IMAGE_LOADER_ID, null, new LoaderManager.LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { //构造筛选语句 String selection = ""; for (int i = 0; i < selections.length; i++) { if (i != 0) { selection = selection + " OR "; } if(isQueryByFormat){ selection = selection + MediaStore.Files.FileColumns.DATA + " LIKE '%" + selections[i] + "'"; }else { selection = selection + MediaStore.Files.FileColumns.DATA + " LIKE '%" + selections[i] + "%'"; } } //按图片修改时间递增顺序对结果进行排序;待会从后往前移动游标就可实现时间递减 String sortOrder = MediaStore.Files.FileColumns.DATE_ADDED;//根据添加时间递增 CursorLoader imageCursorLoader = new CursorLoader(applicationContext, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, null, sortOrder); return imageCursorLoader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if (data == null){ callBack.getDataFailed("查询失败!"); return; } List<Map<String,Object>> list = new ArrayList<>(); Map<String,Object> dataMap; //游标从最后开始往前递减,以此实现时间递减顺序(最近访问的文件,优先显示) if (data.moveToLast()) { do { dataMap = new HashMap<>(); for(int i=0;i<projection.length;i++){ dataMap.put(projection[i],data.getString(i)); } // dataMap.put("path",data.getString(0)); list.add(dataMap); } while (data.moveToPrevious()); } callBack.getListDataSuccess(list);//回调 Presenter层方法 } @Override public void onLoaderReset(Loader<Cursor> loader) { } }); } }
View层的实现
最后,用户只需要在View层调用presenter.getData方法并在相应的接口方法中编写处理数据的逻辑即可new InterfaceManager.SearchDataCallBack() { @Override public void onFinish(List<Map<String, Object>> list) { Log.e("Tag","size:"+list.size()); for(int i=0;i<list.size();i++){ Log.e("DATA"+i,list.get(i).get(MediaStore.Images.Media.DATA).toString()); } } })
整个图片搜索的实现过程就是这样了,至于PhotoFactory是怎样封装这个过程的大家可以去看下源码,代码不难,没有太多层的回调,并且关键的地方我都给了详细的注释,相信大家都能看懂。有啥疑问或建议欢迎留言评论,感激不尽。如果觉得写得还不错麻烦点个赞,你们的支持是我最大的动力~
相关文章推荐
- Android实现获取本机中所有图片(Loader,CursorLoader,LoaderManager,SimpleCursorAdapter的简单应用)
- Android使用加载器(Loader)实现获取本机中所有图片
- Android 使用Loader轻松实现仿微信图片加载
- Android MVP架构(Volley+CursorLoader+ContentProvider)
- 实现获取本机所有图片 - Android - Loader
- Android 框架设计Demo,一个简单的MVP示例搜索功能,网络请求用Retrofit+RxJava实现
- Android RecyclerView+StaggeredGridLayoutManager实现瀑布流图片闪烁问题
- 在Android中使用加载器(Loader)来实现获取本机中的所有图片,并进行查看图片的效果
- android-universal-image-loader缓存中图片的分享操作实现
- Android--Universal-Image-Loader异步图片加载框架封装思路及实现
- Android_Volley+Image-Loader+RecyclerView实现网络下载图片瀑布流
- 实例完成Universal_Iamge_loader框架实现android图片的缓存
- Android - 一种相似图片搜索算法的实现
- Android HAL实现的三种方式(3) - 基于Manager的HAL设计
- Android 开发实例:图片拖动的实现
- 【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- Android 从Internet获取数据 实现获取一张图片
- 【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- 转载:Android实现ListView异步加载图片
- 【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!