1.概述
现在好多的app中都需要实现照片,视频的选择功能,例如头像的修改。网上找了许多例子都不太尽如人意,决定自己实现一下,博客记录一下,也加深自己的记忆。好了,先看一下效果图吧
简单描述一下:
1.默认显示的是所有的照片跟视频,当然了,可以手动选择显示所有的图片还是所有视频还是所有图片和视频。也可以手动选择是单选还是多选,只要在外部通过intent传递两个参数就可以了
2.点击上面的title,也就是所有图片的字,可以显示一个popupwindow,这个popupwindow显示了手机中所有的相册列表,你可以手动选择显示那个相册。所有的图片,视频,单独做成了一个文件夹。
2.上代码
首先是图片的实体类:
public class ImageBean implements Serializable {
private int id;
private String path; //路径
private boolean isVideo;//是否为视频
private boolean isChecked = false;//是否选中
public Object getTragetHolder() {
return tragetHolder;
}
public void setTragetHolder(Object tragetHolder) {
this.tragetHolder = tragetHolder;
}
private Object tragetHolder = null;
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
public boolean isVideo() {
return isVideo;
}
public void setVideo(boolean video) {
isVideo = video;
}
public ImageBean(String path) {
this.path = path;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
图片文件夹实体类:
public class ImageFolder implements Serializable {
/* 文件夹名 */
private String name;
/* 文件夹路径 */
private String dirPath;
/* 该文件夹下图片列表 */
private List<ImageBean> photoList;
/* 标识是否选中该文件夹 */
private boolean isSelected;
public boolean isSelected() {
return isSelected;
}
public void setIsSelected(boolean isSelected) {
this.isSelected = isSelected;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDirPath() {
return dirPath;
}
public void setDirPath(String dirPath) {
this.dirPath = dirPath;
}
public List<ImageBean> getPhotoList() {
return photoList;
}
public void setPhotoList(List<ImageBean> photoList) {
this.photoList = photoList;
}
参数就不一一解释了,相信大家都能看懂类中的参数的作用。
下面是关键的一个工具类:
所有的图片,视频逻辑查询
public class PhotoUtil {
private static Map<String, ImageFolder> folderMap = new HashMap<>();
/**
* 扫描所有图片
* @param context
* @return
*/
public static Map<String, ImageFolder> getPhotos(Context context) {
String allPhotosKey = "所有图片";
ImageFolder allFolder = new ImageFolder();
allFolder.setName(allPhotosKey);
allFolder.setDirPath(allPhotosKey);
allFolder.setPhotoList(new ArrayList<ImageBean>());
folderMap.put(allPhotosKey, allFolder);
getPhoto(context,allPhotosKey);
return folderMap;
}
/**
* 扫描所有视频
* @param context
* @return
*/
public static Map<String,ImageFolder> getVideos(Context context){
String allVideosKey = "所有视频";
ImageFolder allFolder = new ImageFolder();
allFolder.setName(allVideosKey);
allFolder.setDirPath(allVideosKey);
allFolder.setPhotoList(new ArrayList<ImageBean>());
folderMap.put(allVideosKey, allFolder);
getVideo(context,allVideosKey);
return folderMap;
}
/**
* 扫描所有图片和视频
* @param context
* @return
*/
public static Map<String, ImageFolder> getPhotosAndVideos(Context context) {
String allKey = "所有图片和视频";
ImageFolder allVideoFolder = new ImageFolder();
allVideoFolder.setName(allKey);
allVideoFolder.setDirPath(allKey);
allVideoFolder.setPhotoList(new ArrayList<ImageBean>());
folderMap.put(allKey, allVideoFolder);
getPhoto(context,allKey);
getVideo(context,allKey);
return folderMap;
}
public static void getPhoto(Context context,String allPhotosKey){
Uri imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = context.getContentResolver();
String[] projImage = { MediaStore.Images.Media._ID
, MediaStore.Images.Media.DATA
,MediaStore.Images.Media.SIZE
,MediaStore.Images.Media.DISPLAY_NAME};
// 只查询jpeg和png的图片 //"image/mp4","image/3gp"
Cursor mCursor = mContentResolver.query(imageUri, projImage,
MediaStore.Images.Media.MIME_TYPE + " in(?, ?, ?)",
new String[] { "image/jpeg", "image/png", "image/jpg"},
MediaStore.Images.Media.DATE_MODIFIED + " desc");
int pathIndex = mCursor
.getColumnIndex(MediaStore.Images.Media.DATA);
if (mCursor.moveToFirst()) {
do {
// 获取图片的路径
String path = mCursor.getString(pathIndex);
// 获取该图片的父路径名
File parentFile = new File(path).getParentFile();
if (parentFile == null) {
continue;
}
String dirPath = parentFile.getAbsolutePath();
if (folderMap.containsKey(dirPath)) {
ImageBean photo = new ImageBean(path);
photo.setVideo(false);
ImageFolder photoFolder = folderMap.get(dirPath);
photoFolder.getPhotoList().add(photo);
folderMap.get(allPhotosKey).getPhotoList().add(photo);
continue;
} else {
// 初始化imageFolder
ImageFolder photoFolder = new ImageFolder();
List<ImageBean> photoList = new ArrayList<>();
ImageBean photo = new ImageBean(path);
photo.setVideo(false);
photoList.add(photo);
photoFolder.setPhotoList(photoList);
photoFolder.setDirPath(dirPath);
photoFolder.setName(dirPath.substring(dirPath.lastIndexOf(File.separator) + 1, dirPath.length()));
folderMap.put(dirPath, photoFolder);
folderMap.get(allPhotosKey).getPhotoList().add(photo);
}
} while (mCursor.moveToNext());
}
mCursor.close();
}
public static void getVideo(Context context,String allVideoSKey) {
Uri videoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = context.getContentResolver();
String[] projVideo = {MediaStore.Video.Thumbnails._ID
, MediaStore.Video.Thumbnails.DATA
, MediaStore.Video.Media.DURATION
, MediaStore.Video.Media.SIZE
, MediaStore.Video.Media.DISPLAY_NAME
, MediaStore.Video.Media.DATE_MODIFIED};
Cursor mCursor = mContentResolver.query(videoUri, projVideo,
MediaStore.Video.Media.MIME_TYPE + " in(?, ?, ?, ?)",
new String[]{"video/mp4", "video/3gp", "video/avi", "video/rmvb"},
MediaStore.Video.Media.DATE_MODIFIED + " desc");
if (mCursor != null) {
while (mCursor.moveToNext()) {
// 获取视频的路径
String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Video.Media.DATA));
// 获取该视频的父路径名
String dirPath = new File(path).getParentFile().getAbsolutePath();
//存储对应关系
if(folderMap.containsKey(dirPath)){
ImageBean video = new ImageBean(path);
video.setVideo(true);
video.setPath(path);
ImageFolder photoFolder = folderMap.get(dirPath);
photoFolder.getPhotoList().add(video);
folderMap.get(allVideoSKey).getPhotoList().add(video);
}else{
ImageFolder photoFolder = new ImageFolder();
List<ImageBean> photoList = new ArrayList<>();
ImageBean video = new ImageBean(path);
video.setVideo(true);
video.setPath(path);
photoList.add(video);
photoFolder.setPhotoList(photoList);
photoFolder.setDirPath(dirPath);
photoFolder.setName(dirPath.substring(dirPath.lastIndexOf(File.separator) + 1, dirPath.length()));
folderMap.put(dirPath, photoFolder);
folderMap.get(allVideoSKey).getPhotoList().add(video);
}
}
mCursor.close();
}
}
}
首先定义一个map,key为图片文件夹名称,value为装图片的list。通过getPhoto,getVideo两个方法将手机中所有的图片视频根据不同的文件存放在map中。查询的原理其实是用过ContentResolver根据
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
或
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
查询出的。可以通过new 一个String数组在作为查询的参数,数组中定义要查询的图片或视频格式,比如.jpg,png,mp4,rmvb等。外部activity只需要调用PhotoUtil中的方法便可获得存有所有图片的map(通过文件夹分开)。
下面是activity中调用的代码:
public class ChooseImageActivity extends AppCompatActivity {
private GridView choose_image_gridview;
private TextView choose_image_title_text;
private FrameLayout choose_image_title_frame;
private Map<String, ImageFolder> mFolderMap;
private ChooseImageMyAdapter myAdapter;
// private ChoosePicAdapter myAdapter;
private List<ImageBean> mPhotoLists = new ArrayList<>();
private final static String ALL_PHOTO = "所有图片";
private final static String ALL_VIDEO = "所有视频";
private final static String ALL_PHOTO_AND_VIDEO = "所有图片和视频";
private PopupWindow mListImageDirPopupWindow;
private List<ImageFolder> ImageFolders;
private int SHOW_MODE;
private boolean isSingleChoose = false;//是否单选。默认为false
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_image);
initIntentData();
findViewById();
getPhotosTask.execute();
}
private void initIntentData() {
SHOW_MODE = getIntent().getIntExtra("showVideo",0);
isSingleChoose = getIntent().getBooleanExtra("singleChoose",false);
}
private void findViewById() {
choose_image_gridview = (GridView) findViewById(R.id.choose_image_gridview);
choose_image_gridview.setFocusable(true);
choose_image_title_text = (TextView) findViewById(R.id.choose_image_title_text);
choose_image_title_frame = (FrameLayout) findViewById(R.id.choose_image_title_frame);
btn = (Button) findViewById(R.id.btn);
choose_image_title_text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(isSingleChoose){
myAdapter.oldImageView = null;
myAdapter.oldImageViewYes = null;
for(int i = 0;i < mPhotoLists.size();i++){
mPhotoLists.get(i).setChecked(false);
}
}
if(mListImageDirPopupWindow != null && mListImageDirPopupWindow.isShowing()){
mListImageDirPopupWindow.dismiss();
}else{
myAdapter.notifyDataSetChanged();
showFolderPopopWindow();
}
}
});
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
List<String> picPath = myAdapter.getImagePath();
for(int i = 0;i < picPath.size();i++){
Log.i("xxx","path " + picPath.get(i));
}
}
});
}
/**
* 获取照片的异步任务
*/
private AsyncTask getPhotosTask = new AsyncTask() {
@Override
protected void onPreExecute() {
// mProgressDialog = ProgressDialog.show(PhotoPickerActivity.this, null, "loading...");
}
@Override
protected Object doInBackground(Object[] params) {
if(SHOW_MODE == 0){//显示全部
mFolderMap = PhotoUtil.getPhotosAndVideos(ChooseImageActivity.this.getApplicationContext());
}else if(SHOW_MODE == 1){//仅图片
mFolderMap = PhotoUtil.getPhotos(ChooseImageActivity.this.getApplicationContext());
}else if(SHOW_MODE == 2){//仅视频
mFolderMap = PhotoUtil.getVideos(ChooseImageActivity.this.getApplicationContext());
}
return null;
}
@Override
protected void onPostExecute(Object o) {
getPhotosSuccess();
}
};
/**
* 加载照片成功
*/
private void getPhotosSuccess() {
if(SHOW_MODE == 0){//图片和视频
choose_image_title_text.setText(ALL_PHOTO_AND_VIDEO);
mPhotoLists.addAll(mFolderMap.get(ALL_PHOTO_AND_VIDEO).getPhotoList());
}else if(SHOW_MODE == 1){//仅图片
choose_image_title_text.setText(ALL_PHOTO);
mPhotoLists.addAll(mFolderMap.get(ALL_PHOTO).getPhotoList());
}else if(SHOW_MODE == 2){//仅视频
choose_image_title_text.setText(ALL_VIDEO);
mPhotoLists.addAll(mFolderMap.get(ALL_VIDEO).getPhotoList());
}
myAdapter = new ChooseImageMyAdapter(this.getApplicationContext(), mPhotoLists,isSingleChoose);
choose_image_gridview.setAdapter(myAdapter);
Set<String> keys = mFolderMap.keySet();
final List<ImageFolder> folders = new ArrayList<>();
for (String key : keys) {
if (ALL_PHOTO.equals(key) || ALL_PHOTO_AND_VIDEO.equals(key) || ALL_VIDEO.equals(key)) {
ImageFolder folder = mFolderMap.get(key);
folder.setIsSelected(true);
folders.add(0, folder);
} else {
folders.add(mFolderMap.get(key));
}
}
ImageFolders = folders;
choose_image_gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
ImageBean imageBean = myAdapter.getItem(position);
if(imageBean == null) {
return;
}
String path = imageBean.getPath();
Toast.makeText(ChooseImageActivity.this,path,Toast.LENGTH_SHORT).show();
}
});
}
/**
* 显示相册文件夹popupWindow
*/
private void showFolderPopopWindow() {
View popupWindowView = getLayoutInflater().inflate(R.layout.activity_choose_image_list_pop_dir, null);
mListImageDirPopupWindow = new PopupWindow(popupWindowView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, false);
mListImageDirPopupWindow.showAsDropDown(choose_image_title_frame);
ListView dirList = (ListView) popupWindowView.findViewById(R.id.list_pop_dir);
final PopDirListAdapter adapter = new PopDirListAdapter(this,ImageFolders);
dirList.setAdapter(adapter);
mListImageDirPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.alpha = 1f;
getWindow().setAttributes(lp);
}
});
dirList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
ImageFolder folder = ImageFolders.get(position);
adapter.notifyDataSetChanged();
mPhotoLists.clear();
mPhotoLists.addAll(folder.getPhotoList());
choose_image_gridview.setAdapter(myAdapter);
choose_image_title_text.setText(folder.getName());
mListImageDirPopupWindow.dismiss();
}
});
}
@Override
public void finish() {
super.finish();
mFolderMap.clear();
mPhotoLists.clear();
}
因为查询图片是耗时操作,所有需要放在子线程中进行,所以我们放在了异步任务中进行操作。查询成功后,拿到map,遍历将所有的文件夹分开放到List<ImageFolder>中,作为popupwindow的数据源。通过key,拿到所有的图片或视频作为主页显示的数据源。至此基本功能已经实现。可能还会有一些bug,欢迎大家批评指正。
最后附上源码地址:https://gitee.com/fireqiang/NewAlbumChoose