本文文字稍多,源码下载地址在最后。
01 效果图
02 工程目录
03 用到的技术点
LoaderManager、Glide图片加载框架、ButterKnife注解、easypermissions、RecyclerView、PopupWindow、ViewPager
04 一小部分关键代码
图片预览的ViewPager
public class PreviewViewPager extends ViewPager {
private boolean mScrolling;
private float touchDownY;
private int mTouchSlop;
public PreviewViewPager(Context context) {
this(context, null);
}
public PreviewViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
// 由于ViewPager Adapter的Item都有添加点击事件,为了避免上下滑动和点击事件的冲突做如下处理。
// 上下滑动时拦截事件,只有真正的点击时才执行Item的点击事件。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean isBeingDragged = super.onInterceptTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDownY = ev.getY();
mScrolling = false;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(touchDownY - ev.getY()) >= mTouchSlop) {
mScrolling = true;
}
else{
mScrolling = false;
}
break;
case MotionEvent.ACTION_UP:
mScrolling = false;
break;
}
return isBeingDragged ? isBeingDragged : mScrolling;
}
}
相册主页的Fragment
public class AlbumFragment extends BaseFragment implements ImageLoaderListener, View.OnClickListener, BaseRecyclerAdapter.OnItemClickListener{
@BindView(R.id.rv_image) RecyclerView mContentView;
@BindView(R.id.btn_title_select) Button mSelectFolderView;
@BindView(R.id.iv_title_select) ImageView mSelectFolderIcon;
@BindView(R.id.toolbar) View mToolbar;
private LoaderListener mCursorLoader;
private ImageFolderAdapter mImageFolderAdapter;
private ImageAdapter mImageAdapter;
private ImageFolderPopupWindow mFolderPopupWindow;
private String[] mImageSources;
@Override
protected int getLayoutId() {
return R.layout.fragment_select_image;
}
@Override
protected void initWidget(View root) {
super.initWidget(root);
mContentView.setLayoutManager(new GridLayoutManager(getActivity(), 4));
mContentView.addItemDecoration(new SpaceGridItemDecoration((int) DeviceUtil.dipToPx(getResources(), 1)));
mImageAdapter = new ImageAdapter(getContext(), this);
mImageFolderAdapter = new ImageFolderAdapter(getContext(), this);
mContentView.setAdapter(mImageAdapter);
mContentView.setItemAnimator(null);
mImageAdapter.setOnItemClickListener(this);
}
@Override
protected void initData() {
super.initData();
mCursorLoader = new LoaderListener();
getLoaderManager().initLoader(0, null, mCursorLoader);
}
@Override
public void displayImage(ImageView iv, String path) {
// Load image
getImgLoader().load(path)
.asBitmap()
.centerCrop()
.error(R.mipmap.ic_split_graph)
.into(iv);
}
@OnClick({R.id.btn_title_select})
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_title_select:
showPopupFolderList();
break;
}
}
/**
* 创建弹出的相册
*/
private void showPopupFolderList() {
if (mFolderPopupWindow == null) {
ImageFolderPopupWindow popupWindow = new ImageFolderPopupWindow(getContext(), new ImageFolderPopupWindow.Callback() {
@Override
public void onSelect(ImageFolder imageFolder) {
addImagesToAdapter(imageFolder.getImages());
}
@Override
public void onDismiss() {
mSelectFolderIcon.setImageResource(R.mipmap.ic_arrow_bottom);
}
@Override
public void onShow() {
mSelectFolderIcon.setImageResource(R.mipmap.ic_arrow_top);
}
});
popupWindow.setAdapter(mImageFolderAdapter);
mFolderPopupWindow = popupWindow;
}
mFolderPopupWindow.showAsDropDown(mToolbar);
}
@Override
public void onItemClick(int position) {
ImageGalleryActivity.show(getContext(), mImageSources, position);
}
private class LoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
private final String[] IMAGE_PROJECTION = {
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media._ID,
MediaStore.Images.Media.MINI_THUMB_MAGIC,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME};
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id == 0) {
//数据库光标加载器
return new CursorLoader(getContext(),
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION,
null, null, IMAGE_PROJECTION[2] + " DESC");
}
return null;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data != null) {
final ArrayList<Image> images = new ArrayList<>();
final List<ImageFolder> imageFolders = new ArrayList<>();
final ImageFolder defaultFolder = new ImageFolder();
defaultFolder.setName("全部照片");
defaultFolder.setPath("");
imageFolders.add(defaultFolder);
int count = data.getCount();
if (count > 0) {
data.moveToFirst();
do {
String path = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[0]));
String name = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[1]));
long dateTime = data.getLong(data.getColumnIndexOrThrow(IMAGE_PROJECTION[2]));
int id = data.getInt(data.getColumnIndexOrThrow(IMAGE_PROJECTION[3]));
String thumbPath = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[4]));
String bucket = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[5]));
Image image = new Image();
image.setPath(path);
image.setName(name);
image.setDate(dateTime);
image.setId(id);
image.setThumbPath(thumbPath);
image.setFolderName(bucket);
images.add(image);
File imageFile = new File(path);
File folderFile = imageFile.getParentFile();
ImageFolder folder = new ImageFolder();
folder.setName(folderFile.getName());
folder.setPath(folderFile.getAbsolutePath());
if (!imageFolders.contains(folder)) {
folder.getImages().add(image);
folder.setAlbumPath(image.getPath());//默认相册封面
imageFolders.add(folder);
} else {
// 更新
ImageFolder f = imageFolders.get(imageFolders.indexOf(folder));
f.getImages().add(image);
}
} while (data.moveToNext());
}
addImagesToAdapter(images);
defaultFolder.getImages().addAll(images);
defaultFolder.setAlbumPath(images.size() > 0 ? images.get(0).getPath() : null);
mImageFolderAdapter.resetItem(imageFolders);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}
private void addImagesToAdapter(ArrayList<Image> images) {
mImageAdapter.resetItem(images);
mImageSources = toArray(images);
}
private static String[] toArray(List<Image> images) {
if (images == null)
return null;
int len = images.size();
if (len == 0)
return null;
String[] strings = new String[len];
int i = 0;
for (Image image : images) {
strings[i] = image.getPath();
i++;
}
return strings;
}
}
图片文件夹选择(PopupWindow)
public class ImageFolderPopupWindow extends PopupWindow implements View.OnAttachStateChangeListener, BaseRecyclerAdapter.OnItemClickListener{
private ImageFolderAdapter mAdapter;
private RecyclerView mFolderView;
private Callback mCallback;
public ImageFolderPopupWindow(Context context, Callback callback) {
super(LayoutInflater.from(context).inflate(R.layout.popup_window_folder, null),
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mCallback = callback;
// init
setAnimationStyle(R.style.popup_anim_style_alpha);
setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
setOutsideTouchable(true);
setFocusable(true);
// content
View content = getContentView();
content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
content.addOnAttachStateChangeListener(this);
mFolderView = (RecyclerView) content.findViewById(R.id.rv_popup_folder);
mFolderView.setLayoutManager(new LinearLayoutManager(context));
}
public void setAdapter(ImageFolderAdapter adapter) {
this.mAdapter = adapter;
mFolderView.setAdapter(adapter);
mAdapter.setOnItemClickListener(this);
}
@Override
public void onItemClick(int position) {
if (mCallback != null) mCallback.onSelect(mAdapter.getItem(position));
dismiss();
}
@Override
public void onViewAttachedToWindow(View v) {
if(mCallback != null) mCallback.onShow();
}
@Override
public void onViewDetachedFromWindow(View v) {
if(mCallback != null) mCallback.onDismiss();
}
public interface Callback {
void onSelect(ImageFolder imageFolder);
void onDismiss();
void onShow();
}
}
05 源码
源码下载地址。
06 PhotoAlbum apk
PhotoAlbum apk下载地址。