Android基于ImageLoaderUtil封装库的图集实现

前言

之前写了一篇图片加载库的封装案例,其中基于Glide完成了图片加载库ImageLoaderUtil的封装,ImageLoaderUtil提供了诸多图片加载相关的接口:

  • 正常加载图片
  • 针对于GIF图片的特殊加载
  • 加载图片的进度回调
  • 清除缓存
  • 获取缓存大小
  • 图片本地保存
  • 进度回调和本地保存均支持GIF

经过一段时间的检验,ImageLoaderUtil在实际项目中表现良好,并且随着各种问题的暴露、跟进、解决,ImageLoaderUtil也逐渐趋于完善,能够满足项目中大部分关于图片加载的需求,之前的ImageLoaderUtil只是给出了一个很简单的案例来证明这套图片加载库可用,并没有很全面地呈现ImageLoaderUtil真正的价值,因此,我准备基于ImageLoaderUtil来实现一个图集功能,一个大部分APP中都会呈现的一个功能

图集功能简介

一般图集的入口是这样的:(不过这不是重点,我们主要实现点击进去的图集详情页)


这里写图片描述

点击进入是这样的:


这里写图片描述

不要在意数据的不匹配,数据都是随便搞的,哈哈哈

言归正传!
来捋一下一个图集详情页应有的基本功能:

  • 支持图片的手势滑动(ViewPager),多点触控放大缩小(PhotoView)
  • 伴随图片切换的标题简介切换
  • 图集的保存、分享(暂无)、评论(暂无)、收藏(暂无)
  • 当前图集浏览完毕,支持切换到相关的图集推荐(目前很简陋)
  • 图集推荐同时又是一个进入图集详情页的入口

部分功能点分析

Gallery中的页面UI结构


这里写图片描述

参考源码,了解更多

通过ImageLoaderUtil提供的loadImageWithProgress实现图片加载的进度回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void showImage(final View retryView, ImageItem item, final PhotoView photoView, final TextView progressStr, final View progressLayout) {
if (item == null) {
return;
}
String url = item.getImage();
ImageLoaderUtil.getInstance().loadImageWithProgress(url, photoView, new ProgressLoadListener() {
@Override
public void update(int bytesRead, int contentLength) {
progressStr.setText(bytesRead * 100 / contentLength + "%");
}
@Override
public void onException() {
retryView.setVisibility(View.VISIBLE);
progressLayout.setVisibility(View.GONE);
}
@Override
public void onResourceReady() {
progressLayout.setVisibility(View.GONE);
}
});
}

进度加载效果图如下:


这里写图片描述

通过ImageLoaderUtil提供的saveImage实现图片加载的本地保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void saveImage() {
if (list == null) {
ToastUtils.toastCenter(getActivity(), R.string.save_image_fail);
return;
}
final String url = list.get(viewPager.getCurrentItem()).getImage();
ThreadPoolUtils.execute(new Runnable() {
@Override
public void run() {
ImageLoaderUtil.getInstance().saveImage(getActivity(), url,
Environment.getExternalStorageDirectory().getAbsolutePath() + "/gallery",
"pic" + System.currentTimeMillis(), new ImageSaveListener() {
@Override
public void onSaveSuccess() {
handler.obtainMessage(MSG_PIC_SAVE_SUCC).sendToTarget();
}
@Override
public void onSaveFail() {
handler.obtainMessage(MSG_PIC_SAVE_FAIL).sendToTarget();
}
});
}
});
}

效果图如下:


这里写图片描述

Android 六点零 运行时权限处理

上面说到图片的保存功能,自然需要获得内存卡的读写权限,在API23+以上(targetSdkVersion设置到23或者以上时),不止要在AndroidManifest.xml里面添加权限

1
2
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

还得在需要该权限的地方主动调用(详情请参考GalleryFragment,同时需要注意Fragment与Activity在申请运行时权限的不同,Activity中使用ActivityCompat.requestPermissions,而Fragment使用自己本身,如GalleryFragment.this.requestPermissions)
否则会报(java.io.FileNotFoundException:open failed: EACCES (Permission denied))

上图即是在targetSdkVersion设置到23,在6.0系统的手机上运行,弹出授权dialog,只有允许才能保存图片成功,否在会报权限拒绝

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Checks if the app has permission to write to device storage
* <p>
* If the app does not has permission then the user will be prompted to
* grant permissions
*
* @param activity
*/
public void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
GalleryFragment.this.requestPermissions(PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE);
} else {
saveImage();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_EXTERNAL_STORAGE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
saveImage();
} else {
// Permission Denied
ToastUtils.toastCenter(getActivity(), R.string.permission_denied);
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

ViewPaper嵌套相关处理

ViewPaper嵌套使用时,当里面的viewpaper未滑动到最后一个时,
外面的viewpaper禁止滑动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class MyViewPager extends ViewPager {
private OnNeedScrollListener mOnNeedScrollListener;
public MyViewPager(Context context) {
super(context);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setOnNeedScrollListener(OnNeedScrollListener listener){
this.mOnNeedScrollListener = listener;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(mOnNeedScrollListener != null && !mOnNeedScrollListener.needScroll()){
return false;
} else {
try {
return super.onTouchEvent(ev);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
return false;
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if(mOnNeedScrollListener != null && !mOnNeedScrollListener.needScroll()){
return false;
} else {
try {
return super.onInterceptTouchEvent(event);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
return false;
}
}
}
public interface OnNeedScrollListener{
boolean needScroll();
}
}

只有当GalleryFragment中的ViewPager滑动到最后一个,且图集推荐中有内容,才会触发外层ViewPager的滑动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public boolean needScroll() {
if (mCurrFragment instanceof GalleryFragment) {
if (!((GalleryFragment) mCurrFragment).isLastItem()) {
return false;
} else {
//当图集滑动到最后一个时,如果图集推荐没有内容,则禁止滑动
GalleryRelatedFragment relatedFragment = (GalleryRelatedFragment) fragments.get(1);
if (!relatedFragment.isHasData()) {
return false;
}
}
}
return true;
}

源码传送门

github—–>Gallery

csdn—–>Gallery

SuS wechat
欢迎您扫一扫上面的微信公众号,订阅我的最新信息!
坚持原创技术分享,您的支持将鼓励我继续创作!