1.Module介绍
先看效果
1.1项目分析
仿照ImagePicker、PhotoPicker等开源框架,将图片选择器作为一个单独的功能模块解耦出来,作为一个Module,之后可被任意项目引用并使用。先合适的地方( Activity | Fragment )创建Intent并启动,并通过onActivityResult得到所选图片的Path。
整体采用Builder模式进行构建,可以清晰的管理参数,层次清晰,增加代码的可读性。功能支持选择是否单选、多选、拍照;是否进行裁剪,使用到开源库cropview进行图片的裁剪;采用策略模式,支持灵活切换图片加载框架;使用photoview预览大图、手势缩放等等…
1.2如何使用
唤起相册:
new ImagePicker()
.pickType(ImagePickType.SINGLE)
.needCamera(true)
.maxNum(9)
.displayer(new GlideImagePickerDisplayer())
.doCrop(1, 1, 0, 0)
.start(UploadAvatarActivity.this, REQUEST_CODE);
得到数据:
if (requestCode == REQUEST_CODE_UPDATE_HEAD && resultCode == RESULT_OK && data != null) {
List<ImageBean> resultList = data.getParcelableArrayListExtra(ImagePicker.INTENT_RESULT_DATA);
for(ImageBean imageBean : resultList) {
String path = imageBean.getImagePath();
}
}
1.3方法说明
ImagePicker pickType(ImagePickType mode) 设置选择模式
模式:ONLY_CAMERA 拍照、 SINGLE 单选模式、 MULTI 多选模式ImagePicker needCamera(boolean b) 是否需要在界面中显示相机入口
ImagePicker maxNum(int max) 设置图片选择的最大数量
注:拍照和单选都是1,即使设置也无效ImagePicker displayer(IImagePickerDisplayer displayer) 设置图片加载器
注:默认为Glide加载ImagePicker doCrop(int aspectX, int aspectY, int outputX, int outputY) 图片裁剪
参数说明:aspectX、aspectY 裁剪框宽高
outputX、outputY图片输出缩放宽高void start(Activity activity, int requestCode) 发起选择图片
void start(Fragment fragment, int requestCode) 兼容Fragment
1.4目录结构分析
视图层
类名 | 描述 |
---|---|
ImageDataActivity | 主图片展示页面,ViewStub进行视图管理,监听文件夹列表改变刷新数据等 |
ImagePagerActivity | 照片的预览页面 |
ImageCropActivity | 裁剪页 |
ImageFloderPop | 文件夹列表页,采用PopupWindow设计,灵活调整位置 |
data数据层
类名 | 描述 |
---|---|
ImageContants | 常量类,包含各个页面跳转的请求码、数据传递键值、文件名前缀等 |
ImageDataModel | 图片数据层,图片、文件夹等数据管理类 |
ImageBean | 图片实体类 |
ImageFloderBean | 图片文件夹实体类 |
ImagePickerCropParams | 图片裁剪各参数 |
ImagePickerOptions | 图片选择的参数,调用start()方法时,由此类的具体事例进行构建 |
2.Module目录结构
3.部分代码设计
3.1Builder设计
/**
* 通过build模式构造
*
* @author yangjiaming
*/
public class ImagePicker {
/**
* 返回结果中包含图片数据的Intent的键值
*/
public static final String INTENT_RESULT_DATA = "ImageBeans";
private ImagePickerOptions mOptions;
public ImagePicker() {
mOptions = new ImagePickerOptions();
}
/**
* 设置模式
*
* @param mode 拍照、多选、单选
*/
public ImagePicker pickType(ImagePickType mode) {
mOptions.setType(mode);
return this;
}
/**
* 设置多选
*
* @param maxNum 最大数
*/
public ImagePicker maxNum(int maxNum) {
mOptions.setMaxNum(maxNum);
return this;
}
/**
* 是否需要拍照入口
*/
public ImagePicker needCamera(boolean b) {
mOptions.setNeedCamera(b);
return this;
}
public ImagePicker cachePath(String path) {
mOptions.setCachePath(path);
return this;
}
public ImagePicker doCrop(ImagePickerCropParams cropParams) {
mOptions.setNeedCrop(cropParams != null);
mOptions.setCropParams(cropParams);
return this;
}
public ImagePicker doCrop(int aspectX, int aspectY, int outputX, int outputY) {
mOptions.setNeedCrop(true);
mOptions.setCropParams(new ImagePickerCropParams(aspectX, aspectY, outputX, outputY));
return this;
}
public ImagePicker displayer(IImagePickerDisplayer displayer) {
ImageDataModel.getInstance().setDisplayer(displayer);
return this;
}
/**
* 发起选择图片
*
* @param activity 发起的Activity
* @param requestCode 请求码
*/
public void start(Activity activity, int requestCode) {
Intent intent = new Intent(activity, ImageDataActivity.class);
intent.putExtra(ImageContants.INTENT_KEY_OPTIONS, mOptions);
activity.startActivityForResult(intent, requestCode);
}
/**
* 发起选择图片
*
* @param fragment 发起的Fragment
* @param requestCode 请求码
*/
public void start(Fragment fragment, int requestCode) {
Intent intent = new Intent(fragment.getActivity(), ImageDataActivity.class);
intent.putExtra(ImageContants.INTENT_KEY_OPTIONS, mOptions);
fragment.startActivityForResult(intent, requestCode);
}
}
3.2拍照(7.0适配)
/**
* 拍照
*
* @param activity 发起拍照的帮助类
* @param requestCode 请求码
* @param cachePath 缓存地址
* @return 拍照后图片地址
*/
public static String takePhoto(Activity activity, int requestCode, String cachePath) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
//自定义缓存路径
File tempFile = getPhotoTempFile(cachePath);
try {
// 7.0以上需要适配StickMode
if (Build.VERSION.SDK_INT >= 24) {
Uri imageUri = FileProvider.getUriForFile(activity, ImagePickerFileProvider.getAuthorities(activity), tempFile);
// 添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 将拍取的照片保存到指定URI
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
activity.startActivityForResult(intent, requestCode);
} else {
// 将拍取的照片保存到指定URI
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));
activity.startActivityForResult(intent, requestCode);
}
return tempFile.getAbsolutePath();
} catch (Exception e) {
Toast.makeText(activity, R.string.error_can_not_takephoto, Toast.LENGTH_SHORT).show();
return null;
}
}
3.3内容提供者获取照片
// 得到一个游标
ContentResolver cr = context.getContentResolver();
Cursor cur = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
if (cur != null && cur.moveToFirst()) {
// 图片总数
allImgFloder.setNum(cur.getCount());
// 获取指定列的索引
int imageIDIndex = cur.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int imagePathIndex = cur.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
int imageModifyIndex = cur.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED);
int imageWidthIndex = cur.getColumnIndexOrThrow(MediaStore.Images.Media.WIDTH);
int imageHeightIndex = cur.getColumnIndexOrThrow(MediaStore.Images.Media.HEIGHT);
int floderIdIndex = cur.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_ID);
int floderNameIndex = cur.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
do {
String imageId = cur.getString(imageIDIndex);
String imagePath = cur.getString(imagePathIndex);
String lastModify = cur.getString(imageModifyIndex);
String width = cur.getString(imageWidthIndex);
String height = cur.getString(imageHeightIndex);
String floderId = cur.getString(floderIdIndex);
String floderName = cur.getString(floderNameIndex);
if (new File(imagePath).exists()) {
//创建图片对象
ImageBean imageBean = new ImageBean();
imageBean.setImageId(imageId);
imageBean.setImagePath(imagePath);
imageBean.setLastModified(ImagePickerComUtils.isNotEmpty(lastModify) ? Long.valueOf(lastModify) : 0);
imageBean.setWidth(ImagePickerComUtils.isNotEmpty(width) ? Integer.valueOf(width) : 0);
imageBean.setHeight(ImagePickerComUtils.isNotEmpty(height) ? Integer.valueOf(height) : 0);
imageBean.setFloderId(floderId);
mAllImgList.add(imageBean);
//更新文件夹对象
ImageFloderBean floderBean;
if (floderMap.containsKey(floderId)) {
floderBean = floderMap.get(floderId);
} else {
floderBean = new ImageFloderBean(floderId, floderName);
}
floderBean.setFirstImgPath(imagePath);
floderBean.gainNum();
floderMap.put(floderId, floderBean);
}
} while (cur.moveToNext());
cur.close();
}
源码下载
优秀开源项目
[1]ImagePicker https://github.com/Vanish136/ImagePicker
[2]PictureSelector https://github.com/LuckSiege/PictureSelector
[3]ImagePicker https://github.com/jeasonlzy/ImagePicker
[4]TakePhoto https://github.com/crazycodeboy/TakePhoto
[5]PhotoView https://github.com/chrisbanes/PhotoView
[6]cropview https://github.com/oginotihiro/cropview
[7]大杂烩 https://github.com/AbrahamCaiJin/CommonUILibrary/blob/master/README.md