Android 加载大图流程
01.网络请求图片
直接通过http请求网络图片通过流转化成Bitmap。实际开发中一般使用glide去请求加载图片资源。
经过测试,请求8张图片,耗时毫秒值174。如果是服务器响应速度一般,耗时需要2秒【正式接口】。
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/**
* 请求网络图片转化成bitmap
* @param url url
* @return 将url图片转化成bitmap对象
*/
private static long time = 0;
public static Bitmap returnBitMap(String url) {
long l1 = System.currentTimeMillis();
URL myFileUrl = null;
Bitmap bitmap = null;
HttpURLConnection conn = null;
InputStream is = null;
try {
myFileUrl = new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
conn = (HttpURLConnection) myFileUrl.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(5000);
conn.setDoInput(true);
conn.connect();
is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
long l2 = System.currentTimeMillis();
time = (l2-l1) + time;
LogUtils.e("毫秒值"+time);
//保存
}
return bitmap;
}
02.压缩缓存图片
- 这一部分压缩和缓存图片,在glide源码分析的文章里已经做出了比较详细的说明。在这里简单说一下图片请求加载过程……
- 在使用App的时候,会经常需要加载一些网络图片,一般的操作步骤大概是这样的:
- 第一步从网络加载图片:一般都是通过网络拉取的方式去服务器端获取到图片的文件流后,再通过BitmapFactory.decodeStream(InputStreamin)来加载图片Bitmap。
- 第二步这种压缩图片:网络加载图片方式加载一两张图片倒不会出现问题,但是如果短时间内加载十几张或者几十张图片的时候,就很有可能会造成OOM(内存溢出),因为现在的图片资源大小都是非常大的,所以我们在加载图片之前还需要进行相应的图片压缩处理。
- 第三步变换图片:比如需要裁剪,切割圆角,旋转,添加高斯模糊等属性。
- 第四步缓存图片:但又有个问题来了,在使用移动数据的情况下,如果用户每次进入App的时候都会去进行网络拉取图片,这样就会非常的浪费数据流量,这时又需要对图片资源进行一些相应的内存缓存以及磁盘缓存处理,这样不仅节省用户的数据流量,还能加快图片的加载速度。
- 第五步异步加载:虽然利用缓存的方式可以加快图片的加载速度,但当需要加载很多张图片的时候(例如图片墙瀑布流效果),就还需用到多线程来加载图片,使用多线程就会涉及到线程同步加载与异步加载问题。
03.填充到View控件
加载大图的局限性
- 有时候我们通过压缩可以取得很好的效果,但有时候效果就不那么美好了,例如长图像清明上河图,像这类的长图,如果我们直接压缩展示的话,这张图完全看不清,很影响体验。这时我们就可以采用局部展示,然后滑动查看的方式去展示图片。
解决办法
- Android里面是利用BitmapRegionDecoder来局部展示图片的,展示的是一块矩形区域。为了完成这个功能那么就需要一个方法设置图片,另一个方法设置展示的区域。
核心代码如下所示
- BitmapRegionDecoder提供了一系列的newInstance来进行初始化,支持传入文件路径,文件描述符和文件流InputStream等。
- 第二行代码这个方法解决了传入图片,接下来就要去设置展示区域。
1
2mRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
Bitmap bitmap = mRegionDecoder.decodeRegion(mRect, sOptions);最简单的设置
1
2
3
4
5
6
7
8
9try {
BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options1 = new BitmapFactory.Options();
options1.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = regionDecoder.decodeRegion(new Rect(0, 0, getScreenWidth(), getScreenHeight()), options1);
mIvBigPic.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}可以实现区域展示,那我们可不可以自定义一个View,可以随着我们的手指滑动展示图片的不同区域。自定义控件的思路。
- 提供一个设置图片的路口,这个提供给开发者使用
- 重写onTouchEvent,根据用户移动的手势,修改图片显示的区域;
- 每次更新区域参数后,调用invalidate,onDraw里面去regionDecoder.decodeRegion拿到bitmap,去draw
04.自定义图片控件
代码如下所示:
- 在setInputStream方法里面初始BitmapRegionDecoder,获取图片的实际宽高;
- onMeasure方法里面给Rect赋初始化值,控制开始显示的图片区域;
- onTouchEvent监听用户手势,修改Rect参数来修改图片展示区域,并且进行边界检测,最后invalidate;
- 在onDraw里面根据Rect获取Bitmap并且绘制。
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131public class BigImageView extends View {
private static final String TAG = "BigImageView";
private BitmapRegionDecoder mRegionDecoder;
private int mImageWidth, mImageHeight;
private Rect mRect = new Rect();
private static BitmapFactory.Options sOptions = new BitmapFactory.Options();
{
sOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
}
public BigImageView(Context context) {
this(context, null);
}
public BigImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setInputStream(InputStream inputStream) {
try {
mRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
BitmapFactory.decodeStream(inputStream, null, options);
mImageHeight = options.outHeight;
mImageWidth = options.outWidth;
requestLayout();
invalidate();
} catch (IOException e) {
e.printStackTrace();
}
}
int downX = 0;
int downY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int curX = (int) event.getX();
int curY = (int) event.getY();
int moveX = curX - downX;
int moveY = curY - downY;
onMove(moveX, moveY);
System.out.println(TAG + " moveX = " + moveX + " curX = " + curX + " downX = " + downX);
downX = curX;
downY = curY;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
private void onMove(int moveX, int moveY) {
if (mImageWidth > getWidth()) {
mRect.offset(-moveX, 0);
checkWidth();
invalidate();
}
if (mImageHeight > getHeight()) {
mRect.offset(0, -moveY);
checkHeight();
invalidate();
}
}
private void checkWidth() {
Rect rect = mRect;
if (rect.right > mImageWidth) {
rect.right = mImageWidth;
rect.left = mImageWidth - getWidth();
}
if (rect.left < 0) {
rect.left = 0;
rect.right = getWidth();
}
}
private void checkHeight() {
Rect rect = mRect;
if (rect.bottom > mImageHeight) {
rect.bottom = mImageHeight;
rect.top = mImageHeight - getHeight();
}
if (rect.top < 0) {
rect.top = 0;
rect.bottom = getWidth();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
mRect.left = 0;
mRect.top = 0;
mRect.right = width;
mRect.bottom = height;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mRegionDecoder!=null){
Bitmap bitmap = mRegionDecoder.decodeRegion(mRect, sOptions);
canvas.drawBitmap(bitmap, 0, 0, null);
}
}
}