在项目中用到了AsyncTaskLoader来获取本地的图片,没错,就是它,不是AsyncTask;刚看见这个类的时候有些疑惑,想必它肯定和AsyncTask有什么联系,于是网上查找了一些资料,其实它们最主要的区别就是:AsyncTaskLoader只用来获取数据,它不考虑和UI交互,而AsyncTask可以获取数据并将数据展示在UI上。于是,写了一个demo使用AsyncTaskLoader类来获取本地图片。废话少说,进入正题。
首先,需要获取一个加载器的管理对象,然后初始化加载器
getSupportLoaderManager().initLoader(0,null,this);
这通常在onCreate()方法中进行
initLoader的三个参数:
0代表加载器的唯一标识id
null指传递给加载器的参数,这里为null
this指实现了LoaderCallbacks接口的Activity(注意是v4包下面的LoaderCallbacks)
然后接着实现LoaderCallbacks接口的两个方法
/** * 创建数据加载器 * @param id * @param args * @return */ @Override public Loader<List<ImageBean>> onCreateLoader(int id, Bundle args) { return new ImagesLoader(this); }这个是返回带List<ImageBean> 数据的加载器。 ImageBean是具体的数据,它实现了Parcelable接口,便于传递数据
而ImagesLoader就是继承AsyncTaskLoader类的获取本地图片数据的加载器
/** * 数据加载完成之后的操作 * @param loader * @param data */ @Override public void onLoadFinished(Loader<List<ImageBean>> loader, List<ImageBean> data) { mBeans.clear(); mBeans.addAll(data); mAdapter.notifyDataSetChanged(); }这个方法就是数据加载完成之后更新UI
最后就是ImagesLoader类的具体实现,继承AsyncTaskLoader,并实现关键的几个方法。直接上代码吧(代码是最好的老师)
package com.crazy.wang.net; import android.content.Context; import android.database.Cursor; import android.provider.MediaStore; import android.support.v4.content.AsyncTaskLoader; import com.crazy.wang.model.ImageBean; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author Cloudsoar(wangyb) * @datetime 2015-11-30 21:52 GMT+8 * @email 395044952@qq.com */ public class ImagesLoader extends AsyncTaskLoader<List<ImageBean>> { private List<ImageBean> mImages = null; public ImagesLoader(Context context){ super(context); } @Override public List<ImageBean> loadInBackground() { List<ImageBean> imageList = new ArrayList<ImageBean>(); /** * 查询本地图片的数据 */ Cursor imageCursor = getContext().getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID}, null, null, MediaStore.Images.Media._ID); if (imageCursor != null && imageCursor.getCount() > 0) { while (imageCursor.moveToNext()) { ImageBean item = new ImageBean(imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA)), false); imageList.add(item); } } if (imageCursor != null) { imageCursor.close(); } // show newest photo at beginning of the list Collections.reverse(imageList); return imageList; } /* Runs on the UI thread */ @Override public void deliverResult(List<ImageBean> images) { if (isReset()) { // An async query came in while the loader is stopped if (images != null) { images.clear(); images = null; } return; } List<ImageBean> oldImages = mImages; mImages = images; if (isStarted()) { super.deliverResult(images); } if (oldImages != null && oldImages != mImages) { oldImages.clear(); oldImages = null; } } @Override protected void onStartLoading() { if (mImages != null && mImages.size() > 0) { deliverResult(mImages); } if (takeContentChanged() || mImages == null) { forceLoad(); } } /** * Must be called from the UI thread */ @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } @Override public void onCanceled(List<ImageBean> images) { if (images != null) { images.clear(); images = null; } } @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); if (mImages != null) { mImages.clear(); mImages = null; } } }
考虑到以后保存有图片相关信息的ImageBean可以方便的传递,于是让其实现了Parcelable接口。
实现Parcelable接口的注意事项已经写在代码中,已经很详细了,相信大家能看懂
package com.crazy.wang.model; import android.os.Parcel; import android.os.Parcelable; /** * @author Cloudsoar(wangyb) * @datetime 2015-11-30 21:29 GMT+8 * @email 395044952@qq.com */ public class ImageBean implements Parcelable { private String mPath; private boolean isSelected; public ImageBean(String path, boolean isSelected){ this.mPath = path; this.isSelected = isSelected; } /** * * @return 内容描述,返回0即可 */ @Override public int describeContents() { return 0; } /** * 需要传递的数据都需要通过该方法将数据放入Parcel对象中 * 变量放入的顺序必须是变量声明的顺序,否则会报错 * @param dest * @param flags */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPath); dest.writeInt(isSelected ? 1 : 0); } /** * 实现Parcelable接口的bean必须实例化一个Creator对象,且对象名必须是CREATOR */ public static final Creator<ImageBean> CREATOR = new Creator<ImageBean>() { /** * 从Parcel对象中取出数据组装成bean * 这里的数据来自于writeToParcel方法 * 读取数据时必须是按变量声明的顺序 * @param source * @return */ @Override public ImageBean createFromParcel(Parcel source) { return new ImageBean(source.readString(), source.readInt() == 1 ? true : false); } /** * 返回数组bean * @param size * @return */ @Override public ImageBean[] newArray(int size) { return new ImageBean[size]; } }; public String getPath() { return mPath; } public void setPath(String path) { mPath = path; } public boolean isSelected() { return isSelected; } public void setIsSelected(boolean isSelected) { this.isSelected = isSelected; } }
在Adapter中,展示图片的时候,使用到了ImageLoader组件
ImageLoader.getInstance().displayImage("file://" + mBeans.get(position).getPath(), holder.mImageView, DisplayImageOptionsUtil.defaultOptions);
将RecycleView实现为GridView效果很简单,只需要一步搞定(在这里每行显示3张图片)
/** * DividerGridItemDecoration GridView的分割线 */ mRecyclerView.addItemDecoration(new DividerGridItemDecoration(this)); /** * 将RecyclerView设置为GridView的样式 */ mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
最后,项目中用到了两个自定义的组件
DividerGridItemDecoration 将RecycleView实现为GridView效果的时候增加分割线
SquareToWidthRelativeLayout 设置保存图片的容器
这两个类的代码就不贴出了。