1. 概述
现在有很多app都需要上传图片、发表动态等,上传图片需要从手机里边选择需要上传的图片,手机中都自带图片选择器,不过逼格比较高的我们肯定都需要自定义的,说到自定义图片选择器,可以参考微信图片选择器,效果图如下:
2. 思路分析
1>:首先通过ContentProvider扫描SDCard中所有图片,返回一个文件夹列表,封装一个文件夹实体类Folder、一个图片实体类Image,最后通过DataCallback把扫描结果返回,此时就获取到手机中所有的图片;
2>:使用RecyclerView展示图片列表;
3>:然后选择图片;
4>:点击确认把选中的图片通过setResult()方法返回给AActivity,最后使用RecyclerView显示即可;
3. 具体代码如下
1>:首先通过ContentProvider扫描SDCard中所有图片,返回一个文件夹列表,封装一个文件夹实体类Folder、一个图片实体类Image,最后通过DataCallback把扫描结果返回,此时就获取到手机中所有的图片;
/**
* Email: [email protected]
* Created by Novate 2018/4/23 17:10
* Version 1.0
* Params:
* Description: ContentProvider扫描手机图片
*/
public class ImageModel {
/**
* 从SDCard加载图片
*/
public static void loadImageForSDCard(final Context context, final DataCallback callback) {
//由于扫描图片是耗时的操作,所以要在子线程处理。
new Thread(new Runnable() {
@Override
public void run() {
//扫描图片
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = context.getContentResolver();
Cursor mCursor = mContentResolver.query(mImageUri, new String[]{
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media._ID},
null,
null,
MediaStore.Images.Media.DATE_ADDED);
ArrayList images = new ArrayList<>();
//读取扫描到的图片
if (mCursor != null) {
while (mCursor.moveToNext()) {
// 获取图片的路径
String path = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
//获取图片名称
String name = mCursor.getString(
mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
//获取图片时间
long time = mCursor.getLong(
mCursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));
if (!".downloading".equals(getExtensionName(path))) { //过滤未下载完成的文件
images.add(new Image(path, time, name));
}
}
mCursor.close();
}
Collections.reverse(images);
callback.onSuccess(splitFolder(images));
}
}).start();
}
/**
* 把图片按文件夹拆分,第一个文件夹保存所有的图片
*/
private static ArrayList splitFolder(ArrayList images) {
ArrayList folders = new ArrayList<>();
folders.add(new Folder("全部图片", images));
if (images != null && !images.isEmpty()) {
int size = images.size();
for (int i = 0; i < size; i++) {
String path = images.get(i).getPath();
String name = getFolderName(path);
if (StringUtils.isNotEmptyString(name)) {
Folder folder = getFolder(name, folders);
folder.addImage(images.get(i));
}
}
}
return folders;
}
/**
* Java文件操作 获取文件扩展名
*/
public static String getExtensionName(String filename) {
if (filename != null && filename.length() > 0) {
int dot = filename.lastIndexOf('.');
if (dot > -1 && dot < filename.length() - 1) {
return filename.substring(dot + 1);
}
}
return "";
}
/**
* 根据图片路径,获取图片文件夹名称
*/
private static String getFolderName(String path) {
if (StringUtils.isNotEmptyString(path)) {
String[] strings = path.split(File.separator);
if (strings.length >= 2) {
return strings[strings.length - 2];
}
}
return "";
}
private static Folder getFolder(String name, List folders) {
if (!folders.isEmpty()) {
int size = folders.size();
for (int i = 0; i < size; i++) {
Folder folder = folders.get(i);
if (name.equals(folder.getName())) {
return folder;
}
}
}
Folder newFolder = new Folder(name);
folders.add(newFolder);
return newFolder;
}
public interface DataCallback {
void onSuccess(ArrayList folders);
}
}
/**
* Email: [email protected]
* Created by Novate 2018/4/23 17:18
* Version 1.0
* Params:
* Description: 图片文件夹实体类
*/
public class Folder {
private String name;
private ArrayList images;
public Folder(String name) {
this.name = name;
}
public Folder(String name, ArrayList images) {
this.name = name;
this.images = images;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ArrayList getImages() {
return images;
}
public void setImages(ArrayList images) {
this.images = images;
}
public void addImage(Image image) {
if (image != null && StringUtils.isNotEmptyString(image.getPath())) {
if (images == null) {
images = new ArrayList<>();
}
images.add(image);
}
}
@Override
public String toString() {
return "Folder{" +
"name='" + name + '\'' +
", images=" + images +
'}';
}
}
/**
* Email: [email protected]
* Created by Novate 2018/4/23 17:19
* Version 1.0
* Params:
* Description: 图片实体类
*/
public class Image implements Parcelable {
private String path;
private long time;
private String name;
public Image(String path, long time, String name) {
this.path = path;
this.time = time;
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.path);
dest.writeLong(this.time);
dest.writeString(this.name);
}
protected Image(Parcel in) {
this.path = in.readString();
this.time = in.readLong();
this.name = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public Image createFromParcel(Parcel source) {
return new Image(source);
}
@Override
public Image[] newArray(int size) {
return new Image[size];
}
};
}
2>:使用RecyclerView展示图片列表:
/**
* 初始化图片列表
*/
private void initImageList() {
// 判断屏幕方向
Configuration configuration = getResources().getConfiguration();
if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
mLayoutManager = new GridLayoutManager(this, 3);
} else {
mLayoutManager = new GridLayoutManager(this, 5);
}
rvImage.setLayoutManager(mLayoutManager);
mAdapter = new ImageAdapter(this, mMaxCount, isSingle);
rvImage.setAdapter(mAdapter);
((SimpleItemAnimator) rvImage.getItemAnimator()).setSupportsChangeAnimations(false);
if (mFolders != null && !mFolders.isEmpty()) {
setFolder(mFolders.get(0));
}
mAdapter.setOnImageSelectListener(new ImageAdapter.OnImageSelectListener() {
@Override
public void OnImageSelect(Image image, boolean isSelect, int selectCount) {
setSelectImageCount(selectCount);
}
});
mAdapter.setOnItemClickListener(new ImageAdapter.OnItemClickListener() {
@Override
public void OnItemClick(Image image, int position) {
toPreviewActivity(mAdapter.getData(), position);
}
});
}
/**
* Email: [email protected]
* Created by Novate 2018/4/23 17:23
* Version 1.0
* Params:
* Description: 图片选择器列表
*/
public class ImageAdapter extends RecyclerView.Adapter {
private Context mContext;
private ArrayList mImages;
private LayoutInflater mInflater;
//保存选中的图片
private ArrayList mSelectImages = new ArrayList<>();
private OnImageSelectListener mSelectListener;
private OnItemClickListener mItemClickListener;
private int mMaxCount;
private boolean isSingle;
/**
* @param maxCount 图片的最大选择数量,小于等于0时,不限数量,isSingle为false时才有用。
* @param isSingle 是否单选
*/
public ImageAdapter(Context context, int maxCount, boolean isSingle) {
mContext = context;
this.mInflater = LayoutInflater.from(mContext);
mMaxCount = maxCount;
this.isSingle = isSingle;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.adapter_images_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final Image image = mImages.get(position);
Glide.with(mContext).load(new File(image.getPath()))
.apply(new RequestOptions().diskCacheStrategy(DiskCacheStrategy.NONE))
.into(holder.ivImage);
setItemSelect(holder, mSelectImages.contains(image));
//点击选中/取消选中图片
holder.ivSelectIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mSelectImages.contains(image)) {
//如果图片已经选中,就取消选中
unSelectImage(image);
setItemSelect(holder, false);
} else if (isSingle) {
//如果是单选,就先清空已经选中的图片,再选中当前图片
clearImageSelect();
selectImage(image);
setItemSelect(holder, true);
} else if (mMaxCount <= 0 || mSelectImages.size() < mMaxCount) {
//如果不限制图片的选中数量,或者图片的选中数量
// 还没有达到最大限制,就直接选中当前图片。
selectImage(image);
setItemSelect(holder, true);
}else if (mSelectImages.size() == mMaxCount){ // 自己新添加的
Toast.makeText(mContext , "图片最多可选9张" , Toast.LENGTH_SHORT).show();
}
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.OnItemClick(image, holder.getAdapterPosition());
}
}
});
}
/**
* 选中图片
*
* @param image
*/
private void selectImage(Image image) {
mSelectImages.add(image);
if (mSelectListener != null) {
mSelectListener.OnImageSelect(image, true, mSelectImages.size());
}
}
/**
* 取消选中图片
*
* @param image
*/
private void unSelectImage(Image image) {
mSelectImages.remove(image);
if (mSelectListener != null) {
mSelectListener.OnImageSelect(image, false, mSelectImages.size());
}
}
@Override
public int getItemCount() {
return mImages == null ? 0 : mImages.size();
}
public ArrayList getData() {
return mImages;
}
public void refresh(ArrayList data) {
mImages = data;
notifyDataSetChanged();
}
/**
* 设置图片选中和未选中的效果
*/
private void setItemSelect(ViewHolder holder, boolean isSelect) {
if (isSelect) {
holder.ivSelectIcon.setImageResource(R.drawable.icon_image_select);
holder.ivMasking.setAlpha(0.5f);
} else {
holder.ivSelectIcon.setImageResource(R.drawable.icon_image_un_select);
holder.ivMasking.setAlpha(0.2f);
}
}
private void clearImageSelect() {
if (mImages != null && mSelectImages.size() == 1) {
int index = mImages.indexOf(mSelectImages.get(0));
if (index != -1) {
mSelectImages.clear();
notifyItemChanged(index);
}
}
}
public void setSelectedImages(ArrayList selected) {
if (mImages != null && selected != null) {
for (String path : selected) {
if (isFull()) {
return;
}
for (Image image : mImages) {
if (path.equals(image.getPath())) {
if (!mSelectImages.contains(image)) {
mSelectImages.add(image);
}
break;
}
}
}
notifyDataSetChanged();
}
}
private boolean isFull() {
if (isSingle && mSelectImages.size() == 1) {
return true;
} else if (mMaxCount > 0 && mSelectImages.size() == mMaxCount) {
return true;
} else {
return false;
}
}
public ArrayList getSelectImages() {
return mSelectImages;
}
public void setOnImageSelectListener(OnImageSelectListener listener) {
this.mSelectListener = listener;
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.mItemClickListener = listener;
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView ivImage;
ImageView ivSelectIcon;
ImageView ivMasking;
public ViewHolder(View itemView) {
super(itemView);
ivImage = itemView.findViewById(R.id.iv_image);
ivSelectIcon = itemView.findViewById(R.id.iv_select);
ivMasking = itemView.findViewById(R.id.iv_masking);
}
}
public interface OnImageSelectListener {
void OnImageSelect(Image image, boolean isSelect, int selectCount);
}
public interface OnItemClickListener {
void OnItemClick(Image image, int position);
}
}
3>:然后选择图片;
setItemSelect(holder, mSelectImages.contains(image));
//点击选中/取消选中图片
holder.ivSelectIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mSelectImages.contains(image)) {
//如果图片已经选中,就取消选中
unSelectImage(image);
setItemSelect(holder, false);
} else if (isSingle) {
//如果是单选,就先清空已经选中的图片,再选中当前图片
clearImageSelect();
selectImage(image);
setItemSelect(holder, true);
} else if (mMaxCount <= 0 || mSelectImages.size() < mMaxCount) {
//如果不限制图片的选中数量,或者图片的选中数量
// 还没有达到最大限制,就直接选中当前图片。
selectImage(image);
setItemSelect(holder, true);
}else if (mSelectImages.size() == mMaxCount){ // 自己新添加的
Toast.makeText(mContext , "图片最多可选9张" , Toast.LENGTH_SHORT).show();
}
}
});
/**
* 选中图片
*
* @param image
*/
private void selectImage(Image image) {
mSelectImages.add(image);
if (mSelectListener != null) {
mSelectListener.OnImageSelect(image, true, mSelectImages.size());
}
}
/**
* 取消选中图片
*
* @param image
*/
private void unSelectImage(Image image) {
mSelectImages.remove(image);
if (mSelectListener != null) {
mSelectListener.OnImageSelect(image, false, mSelectImages.size());
}
}
/**
* 设置图片选中和未选中的效果
*/
private void setItemSelect(ViewHolder holder, boolean isSelect) {
if (isSelect) {
holder.ivSelectIcon.setImageResource(R.drawable.icon_image_select);
holder.ivMasking.setAlpha(0.5f);
} else {
holder.ivSelectIcon.setImageResource(R.drawable.icon_image_un_select);
holder.ivMasking.setAlpha(0.2f);
}
}
4>:点击确认把选中的图片通过setResult()方法返回给AActivity,最后使用RecyclerView显示即可;
private void confirm() {
if (mAdapter == null) {
return;
}
//因为图片的实体类是Image,而我们返回的是String数组,所以要进行转换。
ArrayList selectImages = mAdapter.getSelectImages();
ArrayList images = new ArrayList<>();
for (Image image : selectImages) {
images.add(image.getPath());
}
//点击确定,把选中的图片通过Intent传给上一个Activity。
Intent intent = new Intent();
intent.putStringArrayListExtra(ImageSelectorUtils.SELECT_RESULT, images);
setResult(RESULT_OK, intent);
finish();
}
具体代码已上传至github:
https://github.com/shuai999/ImageSelector
备注
这篇文章是自己参考一个大神整理的,并非自己原创,代码也是参考大神的,自己在这里只是整理,作为学习资料,仅此而已。
详情可参考大神博客:
Android 实现一个仿微信的图片选择器