版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】怎么使用Android实现微信的图片选择器
在下给大家分享一下怎么使用Android实现微信的图片选择器,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!现在大部分的App都上传图片的功能,比如设置用户头像、聊天发送图片、发表动态、论坛帖子等。上传图片需要先从选择手机中选择要上传的图片,所以图片选择器在App中是很常见的组件,一般的手机都会自带一个图片选择器。不过很多App并不喜欢用手机自带的选择器,而是自己实现一个图片选择器。比如微信的图片选择器就做的很好。所以我也仿照微信的样式和交互效果,自己做了一个图片选择器:ImageSelector。ImageSelector支持图片的单选、限数量的多选和不限数量的多选。支持图片预览和图片文件夹的切换。项目已经上传到了我的GitHub,欢迎大家下载和使用。本篇文章我将为大家介绍ImageSelector是如何现实的。由于ImageSelector的项目代码比较多,所以这篇文章只讲解ImageSelector的实现思路和分析主要的项目代码。想要看完整代码的同学请到GitHub下载项目。至于ImageSelector的使用,在我的另一篇文章中有详细的介绍:《Android仿微信的图片选择器ImageSelector的使用》。先上效果图:要实现一个图片选择器,需要做的主要就是以下几个事情:1、从手机存储卡中扫描加载图片。2、用一个列表将图片显示出来。3、选择图片。4、把选中的图片返回给调用者。准备工作首先需要导入两个第三方库。//图片加载库,用于加载图片
compile
'com.github.bumptech.glide:glide:3.7.0'
//图片缩放库,用于图片的预览
compile
'com.github.chrisbanes:PhotoView:2.0.0'第一步:从手机存储卡中扫描加载图片读取手机存储卡需要先申请权限:<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>对Android6.0以上的系统做动态权限处理。
/**
*
检查权限并加载SD卡里的图片。
*/
private
void
checkPermissionAndLoadImages()
{
int
hasWriteContactsPermission
=
ContextCompat.checkSelfPermission(getApplication(),
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if
(hasWriteContactsPermission
==
PackageManager.PERMISSION_GRANTED)
{
//有权限,加载图片。
loadImageForSDCard();
}
else
{
//没有权限,申请权限。
ActivityCompat.requestPermissions(ImageSelectorActivity.this,
new
String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSION_REQUEST_CODE);
}
}
/**
*
处理权限申请的回调。
*/
@Override
public
void
onRequestPermissionsResult(int
requestCode,
String[]
permissions,
int[]
grantResults)
{
if
(requestCode
==
PERMISSION_REQUEST_CODE)
{
if
(grantResults.length
>
0
&&
grantResults[0]
==
PackageManager.PERMISSION_GRANTED)
{
//允许权限,加载图片。
loadImageForSDCard();
}
else
{
//拒绝权限,弹出提示框。
showExceptionDialog();
}
}
}接着就是从手机存储卡中扫描加载图片。
/**
*
从SDCard加载图片。
*/
private
void
loadImageForSDCard()
{
ImageModel.loadImageForSDCard(this,
new
ImageModel.DataCallback()
{
@Override
public
void
onSuccess(ArrayList<Folder>
folders)
{
//folders是图片文件夹的列表,每个文件夹中都有若干张图片。
}
});
}我将扫描手机存储卡的操作封装在ImageModel类,并通过DataCallback把扫描的结果返回给调用者。返回的结果是一个文件夹的列表,这是因为我们的图片选择器需要实现文件夹切换的功能,所以我把扫描出来的图片按文件夹进行了拆分。下面看文件夹实体类Folder和图片实体类Image:/**
*
图片文件夹实体类
*/
public
class
Folder
{
private
String
name;
private
ArrayList<Image>
images;
public
Folder(String
name)
{
=
name;
}
public
Folder(String
name,
ArrayList<Image>
images)
{
=
name;
this.images
=
images;
}
public
String
getName()
{
return
name;
}
public
void
setName(String
name)
{
=
name;
}
public
ArrayList<Image>
getImages()
{
return
images;
}
public
void
setImages(ArrayList<Image>
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);
}
}
}
/**
*图片实体类
*/
public
class
Image
{
private
String
path;
private
long
time;
private
String
name;
public
Image(String
path,
long
time,
String
name)
{
this.path
=
path;
this.time
=
time;
=
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)
{
=
name;
}
}注意,最核心的代码来了:使用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<Image>
images
=
new
ArrayList<>();
//读取扫描到的图片
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));
images.add(new
Image(path,
time,
name));
}
mCursor.close();
Collections.reverse(images);
callback.onSuccess(splitFolder(images));
}
}).start();
}
/**
*
把图片按文件夹拆分,第一个文件夹保存所有的图片
*/
private
static
ArrayList<Folder>
splitFolder(ArrayList<Image>
images)
{
ArrayList<Folder>
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;
}
/**
*
根据图片路径,获取图片文件夹名称
*/
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<Folder>
folders)
{
if
(folders
!=
null
&&
!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<Folder>
folders);
}
}到这里,我们就已经拿到了手机里的所有图片了,接下来就是展示图片列表。第二步:展示图片这个非常的简单,其实就是写一个图片列表。
//图片列表
private
RecyclerView
rvImage;
//
初始化图片列表
private
void
initImageList()
{
mLayoutManager
=
new
GridLayoutManager(this,
3);
rvImage.setLayoutManager(mLayoutManager);
mAdapter
=
new
ImageAdapter(this,
mMaxCount,
isSingle);
rvImage.setAdapter(mAdapter);
}这里的ImageAdapter,就是图片列表的Adapter。public
class
ImageAdapter
extends
RecyclerView.Adapter<ImageAdapter.ViewHolder>
{
private
Context
mContext;
private
ArrayList<Image>
mImages;
private
LayoutInflater
mInflater;
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,
final
int
position)
{
final
Image
image
=
mImages.get(position);
Glide.with(mContext).load(new
File(image.getPath()))
.diskCacheStrategy(DiskCacheStrategy.NONE).into(holder.ivImage);
}
@Override
public
int
getItemCount()
{
return
mImages
==
null
?
0
:
mImages.size();
}
static
class
ViewHolder
extends
RecyclerView.ViewHolder
{
ImageView
ivImage;
ImageView
ivSelectIcon;
ImageView
ivMasking;
public
ViewHolder(View
itemView)
{
super(itemView);
ivImage
=
(ImageView)
itemView.findViewById(R.id.iv_image);
ivSelectIcon
=
(ImageView)
itemView.findViewById(R.id.iv_select);
ivMasking
=
(ImageView)
itemView.findViewById(R.id.iv_masking);
}
}
}第三步:选择图片点击选择图片,主要是对图片列表的ImageAdapter中的item进行操作,所以我就直接写在了ImageAdapter类里。选中的图片用一个数组保存。
//保存选中的图片
private
ArrayList<Image>
mSelectImages
=
new
ArrayList<>();
//点击选中/取消选中图片
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);
}
}
});
/**
*
选中图片
*/
private
void
selectImage(Image
image){
mSelectImages.add(image);
}
/**
*
取消选中图片
*/
private
void
unSelectImage(Image
image
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024-2030年电动手术手机行业市场现状供需分析及重点企业投资评估规划分析研究报告
- 2024-2030年甲型肝炎疫苗行业市场现状供需分析及重点企业投资评估规划分析研究报告
- 2024-2030年生物电诊断仪器市场前景分析及投资策略与风险管理研究报告
- 毕业证明书格式
- 执行异议之诉起诉状
- 同居关系证明书
- 舞蹈教师期末工作总结
- 建筑园区租赁管理规定
- 糖尿病自我管理:常用药表格版
- 环保产业传染病防治应急预案
- 2024年入团积极分子考试题库(含答案)
- 肾上腺占位的护理
- 2024年三支一扶知识考试题(附含答案)
- 高端装配式建筑集成房屋项目商业计划书
- 新能源汽车驱动电机定转子项目可行性研究报告
- 医保飞行检查培训课件
- 第十七章 勾股定理 单元测试卷 2023-2024学年人教版数学八年级下册
- 青马工程题及答案附有答案
- 江苏省兴化市桃花岛矿泉水深井矿山地质环境保护与土地复垦方案
- 小学体育跨学科主题学习教学案例滚铁环
- 冰毯机的使用及注意事项
评论
0/150
提交评论