最近需要做一个图片选择器,发现Coding里面有一个开源的图片选择控件,于是就厚颜无耻的拿出来简单修改一下自己用了,修改之后即可以实现单图片选择裁剪,也能多图片选择。
废话不多说,没图没真相,
裁剪调用的是系统的,原生系统的看起来就是简陋啊
调用起来也是相当的简单,写个intent就行了
//单选裁剪是调用
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, PhotoPickActivity.class);
intent.putExtra(PhotoPickActivity.EXTRA_MODE, PhotoPickActivity.MODE_SINGLE_CROP);
startActivityForResult(intent, PhotoPickActivity.REQUEST_PHOTO_CROP);
}
});
//多选时调用
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, PhotoPickActivity.class);
intent.putExtra(PhotoPickActivity.EXTRA_MODE, PhotoPickActivity.MODE_MUTIL_CROP);
intent.putExtra(PhotoPickActivity.EXTRA_MAX, 6);
intent.putExtra(PhotoPickActivity.EXTRA_PICKED,new ArrayList<ImageInfo>());
startActivityForResult(intent, PhotoPickActivity.REQUEST_PHOTO_LIST);
}
});
//然后在onactivityresult里面获取回调的数据
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == PhotoPickActivity.REQUEST_PHOTO_CROP && resultCode == Activity.RESULT_OK){//获取裁剪后的图片路径
Bundle bundle = data.getExtras();
String string = bundle.getString(PhotoPickActivity.EXTRA_RESULT_CROP_PHOTO);
textView.setText(string);
ImageLoader.getInstance().displayImage(ImageInfo.pathAddPreFix(string), image1, PhotoPickActivity.optionsImage);
}
if(requestCode == PhotoPickActivity.REQUEST_PHOTO_LIST && resultCode == Activity.RESULT_OK){
//图片集合
ArrayList<ImageInfo> imageInfos = (ArrayList<ImageInfo>) data.getSerializableExtra(PhotoPickActivity.EXTRA_RESULT_PHOTO_LIST);
}
}
实现
简单的使用方法说完了,然后说一下它的实现方法.
在这里我使用recycleView代替了listview和gridview,为什么,因为我任性。总是看到什么新的东西就想用用它。
首先说PhotoPickActivity,它就是获取手机的所有图片,然后将它分类,在展示出来,因此,先看看他是怎么获取图片的。
- 首先先获取全部的图片来分析每个文件夹的数量。
private void initDirectory() {
final String[] needInfos = {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
};
//存储每个类别及他里面图片的数量
LinkedHashMap<String, Integer> mNames = new LinkedHashMap<>();
//存储每个类别的第一张图片信息,在目录显示需要
LinkedHashMap<String, ImageInfo> mData = new LinkedHashMap<>();
Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, needInfos, "", null, MediaStore.MediaColumns.DATE_ADDED + " DESC");
while (cursor.moveToNext()) {
String name = cursor.getString(2);
if (!mNames.containsKey(name)) {
mNames.put(name, 1);//每个类别的数目
ImageInfo imageInfo = new ImageInfo(cursor.getString(1));
mData.put(name, imageInfo);//每个类别的第一张照片
} else {
int newCount = mNames.get(name) + 1;
mNames.put(name, newCount);
}
}
ArrayList<ImageInfoExtra> mFolderData = new ArrayList<>();//所有图片
if (cursor.moveToFirst()) {//保存第一个项(所有图片的目录项)
ImageInfo imageInfo = new ImageInfo(cursor.getString(1));
int allImagesCount = cursor.getCount();
mFolderData.add(new ImageInfoExtra(allPhotos, imageInfo, allImagesCount));
}
for (String item : mNames.keySet()) {//保存每个项目图片
ImageInfo info = mData.get(item);
Integer count = mNames.get(item);
mFolderData.add(new ImageInfoExtra(item, info, count));
}
cursor.close();
//显示目录列表信息
mFolderAdapter = new FolderAdapter(mFolderData,this);
mRecycleTitle.setAdapter(mFolderAdapter);
mFolderAdapter.setOnitemClickListener(mOnFolderItemClick);
}
将每个文件夹的名字及里面图片数量保存到一个list里面,因为有个全部图片选项,所以list的第一个item保存所有图片数量并且命名为“全部图片”。
- 接下来获取每个文件夹的图片数量并显示在主界面上,由于默认显示所有图片,所以首先获取全部图片。
这里使用了LoaderManager异步加载相片数据,
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String where ;
//选的不是第一个项目(指全部图片)
if(!isAllPhotoMode()){
String select = ((FolderAdapter) mRecycleTitle.getAdapter()).getSelect();
where = String.format("%s='%s'",
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
select);
}else{
where = "";
}
return new CursorLoader(PhotoPickActivity.this,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
where,
null,
MediaStore.MediaColumns.DATE_ADDED + " DESC");
}
//处理获取到的cursor数据
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if(isAllPhotoMode()){
//全部图片的话,第一个item是照相机,AllPhotoAdapter继承GridPhotoAdapter为了添加照相机item
photoAdapter = new AllPhotoAdapter(this,data);
}else {
photoAdapter = new GridPhotoAdapter(this,data);
}
mRecycleItem.setAdapter(photoAdapter);
photoAdapter.setOnItemClick(mOnPhotoItemClick);
}
通过获取到的数据更新图片。
上面的where参数是为了判断要拿哪一个文件夹里面的所有图片。它来自于文件夹列表的点击事件
private FolderAdapter.ItemClickListener mOnFolderItemClick = new FolderAdapter.ItemClickListener() {
@Override
public void onItemClick(int position,FolderAdapter.FolderHolder holder) {
//更新当前选择的文件夹名称
//主界面根据获取的文件夹名字来加载该文件夹下面的图片
mFolderAdapter.setSelect(position);
mFoldName.setText(mFolderAdapter.getSelect());
hideFolderList();
//每次点击文件夹列表,如果和上次不同的话都获取一次
if(mFolderId != position){
getLoaderManager().destroyLoader(mFolderId);
mFolderId = position;
getLoaderManager().initLoader(mFolderId, null, PhotoPickActivity.this);
}
}
};
最后就是出来图片的点击了。
在多图片选择模式下,图片点击可以预览,同时还有个checkbox可以点击。
由于使用recycleview显示数据,它不能定义item的点击事件,所以我在实例化item view的时候自定义个图片item点击回调和checkbox点击回调。
public void onBindViewHolder(final GridPhotoAdapter.GridPhotoHolder holder,int position) {
final String path = ImageInfo.pathAddPreFix(imageInfos.get(position).path);
final int pos = position;
ImageLoader.getInstance().displayImage(path, holder.icon,
PhotoPickActivity.optionsImage);
if(mActivity.getPhotoMode() == PhotoPickActivity.MODE_SINGLE_CROP){//单图片模式
holder.check.setVisibility(View.GONE);
holder.iconFore.setVisibility(View.GONE);
}else{//多图片模式
boolean isPick = mActivity.isPicked(path);
holder.check.setChecked(isPick);
//图片选中的话添加个蒙版
holder.iconFore.setVisibility(holder.check.isChecked() ? View.VISIBLE : View.GONE);
holder.check.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//设置个checkbox选择回调
itemPhotoClick.onCheckBoxClick(holder.check.isChecked(), path, holder.check);
holder.iconFore.setVisibility(holder.check.isChecked() ? View.VISIBLE : View.GONE);
}
});
}
holder.icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(itemPhotoClick != null){
Log.d("unlock", pos + "setOnClickListener" + path);
itemPhotoClick.onItemClick(pos,path);
}
}
});
}
上面的checkbox点击监听没有使用setOnCheckedChangeListener,因为使用它的时候,进入图片详情界面(PhotoPickDetailActivity)选择图片再回来的时候会发现多选了几个图片,原因怎么都找不到,果断偷懒。
图片的点击处理如下
//图片的点击
//如果是裁剪模式直接调到裁剪页面
//如果是多选模式,则开启viewpager显示当前页面的图片
GridPhotoAdapter.ItemPhotoClick mOnPhotoItemClick = new GridPhotoAdapter.ItemPhotoClick() {
@Override
public void onItemClick(int position,String path) {
if(mPhotoMode == MODE_SINGLE_CROP){//点击图片调用系统裁剪功能
Uri uri = Uri.parse(path);
fileCropUri = CameraPhotoUtil.getOutputMediaFileUri();
cropImageUri(uri, fileCropUri,640,640,REQUEST_RESULT_PHOTO_CROP);
}else{
//进入图片详情界面
Intent intent = new Intent(PhotoPickActivity.this, PhotoPickDetailActivity.class);
intent.putExtra(PhotoPickDetailActivity.PICK_DATA, mPickData);
intent.putExtra(PhotoPickDetailActivity.EXTRA_MAX, mMaxPick);
String folderParam = "";
intent.putExtra(PhotoPickDetailActivity.PHOTO_BEGIN, position);
folderParam = mFolderAdapter.getSelect();
if(folderParam.equals(allPhotos))
folderParam = "";
intent.putExtra(PhotoPickDetailActivity.FOLDER_NAME, folderParam);
startActivityForResult(intent, REQUEST_PHOTO_DETAIL);
}
}
//点击拍照
@Override
public void onCameraClick() {
if(mPickData.size() >= mMaxPick){
Toast.makeText(PhotoPickActivity.this,String.format("最多只能选择%s张",mMaxPick),Toast.LENGTH_SHORT).show();
return;
}
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
fileUri = CameraPhotoUtil.getOutputMediaFileUri();
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(intent, REQUEST_RESULT_PHOTO);
}
//点击checkbox
@Override
public void onCheckBoxClick(boolean isCheck, String path,View view) {
if (isCheck) {
if(mPickData.size()>=mMaxPick){
Toast.makeText(PhotoPickActivity.this,String.format("最多只能选择%s张",mMaxPick),Toast.LENGTH_SHORT).show();
((CheckBox)view).setChecked(false);
return ;
}
//选中图片的list中没有该图片的话,添加它
if (!isPicked(path) && mPickData.size() < mMaxPick) {
ImageInfo imageInfo = new ImageInfo(path);
mPickData.add(imageInfo);
}
} else {//取消checkbox
for (int i = 0; i < mPickData.size(); i++) {
if (mPickData.get(i).path.equals(path)) {
mPickData.remove(i);
}
}
}
updatePickCount();
}
};
点击图片item进入详情页就不多说了,也就是在viewpager显示传过去的图片list,并将处理的list通过setResult放回到PhotoPickActivity,PhotoPickActivity最终也是将数据setResult返回给调用它的activity.
WrapContentLinearLayoutManager:recycleview显示的list设置高度wrap_content时失效,上stackoverflow查了一下,大神们重写了LinearLayoutManager,就可以设置它wrap_content了。
大致介绍就到这里了,大家感兴趣的可以下载源码看看