图片相关

图片压缩后再上传

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
private void uploadHeadPortrait(Uri imgUri) {
compressPic(imgUri);
}

//压缩图片
private void compressPic(Uri uri){
ImageCompress compress = new ImageCompress();
ImageCompress.CompressOptions options = new ImageCompress.CompressOptions();
// options.uri = Uri.fromFile(new File(sourcePath));
options.uri = uri;
options.maxWidth=Constants.RESIZEBITMAP_WIDTH;
options.maxHeight=Constants.RESIZEBITMAP_HEIGHT;
Bitmap bitmap = compress.compressFromUri(ManagerJJActivity.this, options);
saveCompressedPic(bitmap, uri);
}

//bitmap图片保存到手机指定路径
private void saveCompressedPic(Bitmap bitmap, Uri uri){
boolean isSaved = BitmapUtils.setBitmapToFile(bitmap, Constants.COMPRESSED_PIC_PATH);
if (isSaved){//保存成功用压缩后的图片进行上传
Uri uri1 = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "compress.jpg"));
uploadPic(uri1);
}else {//未保存成功,用原图片进行上传
uploadPic(uri);
}
}

//从指定位置捞出来图片进行上传
private void uploadPic(Uri imgUri){
if (null == loadingBar) {
loadingBar = LoadingBar.make(findViewById(R.id.activity_manager_jj_rootlayout), new CustomLoadingFactory());
}
if (null != loadingBar) {
loadingBar.show();
loadingBar.setOnClickListener(null);
}

final String filepath;
if (imgUri.toString().startsWith("file")) {
filepath = imgUri.getPath();
} else {
filepath = UiHelper.getFilePathFromContentUri(imgUri, getContentResolver());
}

//采用okhttp3来进行网络请求
String url = UrlManager.URL_IMAGEUPLOAD;
OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(Constants.OKHTTPS_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
.readTimeout(Constants.OKHTTPS_READ_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(Constants.OKHTTPS_WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
.build();

RequestBody formBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
// .addFormDataPart("title","Square Logo")
.addFormDataPart("file", "logo-square.png", RequestBody.create(MEDIA_TYPE_PNG, new File(filepath))).build();
Request request = new Request.Builder().url(url).post(formBody).build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
dialogCancel();

Message msg = new Message();
msg.what = INFO_WHAT;
msg.obj = Constants.UPLOAD_FAIL;
myHandler.sendMessage(msg);
}

@Override
public void onResponse(Call call, Response response) throws IOException {
dialogCancel();
String str = response.body().string();
Log.i(Constants.TAG, str);

JSONObject jsonObject = JSONObject.parseObject(str);
filePath = (String) jsonObject.get("data");
runOnUiThread(new Runnable() {
@Override
public void run() {
Message msg = new Message();
msg.what = INFO_WHAT;
msg.obj = Constants.UPLOAD_SUCCES;
myHandler.sendMessage(msg);
}
});
}

});
}

上面用到底下的图片压缩Bitmap处理:变圆、读取、存、缩放等

ImageCompress图片压缩

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package rongshanghui.tastebychance.com.rongshanghui.util;

import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**图片压缩工具类
* Created by shenbinghong on 2018/1/12.
*/

public class ImageCompress {
public static final String CONTENT = "content";
public static final String FILE = "file";

/**
* 图片压缩参数
*
* @author Administrator
*
*/
public static class CompressOptions {
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 800;

public int maxWidth = DEFAULT_WIDTH;
public int maxHeight = DEFAULT_HEIGHT;
/**
* 压缩后图片保存的文件
*/
public File destFile;
/**
* 图片压缩格式,默认为jpg格式
*/
public Bitmap.CompressFormat imgFormat = Bitmap.CompressFormat.JPEG;

/**
* 图片压缩比例 默认为30
*/
public int quality = 30;

public Uri uri;
}

public Bitmap compressFromUri(Context context,
CompressOptions compressOptions) {

// uri指向的文件路径
String filePath = getFilePath(context, compressOptions.uri);

if (null == filePath) {
return null;
}

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

Bitmap temp = BitmapFactory.decodeFile(filePath, options);

int actualWidth = options.outWidth;
int actualHeight = options.outHeight;

int desiredWidth = getResizedDimension(compressOptions.maxWidth,
compressOptions.maxHeight, actualWidth, actualHeight);
int desiredHeight = getResizedDimension(compressOptions.maxHeight,
compressOptions.maxWidth, actualHeight, actualWidth);

options.inJustDecodeBounds = false;
options.inSampleSize = findBestSampleSize(actualWidth, actualHeight,
desiredWidth, desiredHeight);

Bitmap bitmap = null;

Bitmap destBitmap = BitmapFactory.decodeFile(filePath, options);

// If necessary, scale down to the maximal acceptable size.
if (destBitmap.getWidth() > desiredWidth
|| destBitmap.getHeight() > desiredHeight) {
bitmap = Bitmap.createScaledBitmap(destBitmap, desiredWidth,
desiredHeight, true);
destBitmap.recycle();
} else {
bitmap = destBitmap;
}

// compress file if need
if (null != compressOptions.destFile) {
compressFile(compressOptions, bitmap);
}

return bitmap;
}

/**
* compress file from bitmap with compressOptions
*
* @param compressOptions
* @param bitmap
*/
private void compressFile(CompressOptions compressOptions, Bitmap bitmap) {
OutputStream stream = null;
try {
stream = new FileOutputStream(compressOptions.destFile);
} catch (FileNotFoundException e) {
Log.e("ImageCompress", e.getMessage());
}

bitmap.compress(compressOptions.imgFormat, compressOptions.quality,
stream);
}

private static int findBestSampleSize(int actualWidth, int actualHeight,
int desiredWidth, int desiredHeight) {
double wr = (double) actualWidth / desiredWidth;
double hr = (double) actualHeight / desiredHeight;
double ratio = Math.min(wr, hr);
float n = 1.0f;
while ((n * 2) <= ratio) {
n *= 2;
}

return (int) n;
}

private static int getResizedDimension(int maxPrimary, int maxSecondary,
int actualPrimary, int actualSecondary) {
// If no dominant value at all, just return the actual.
if (maxPrimary == 0 && maxSecondary == 0) {
return actualPrimary;
}

// If primary is unspecified, scale primary to match secondary's scaling
// ratio.
if (maxPrimary == 0) {
double ratio = (double) maxSecondary / (double) actualSecondary;
return (int) (actualPrimary * ratio);
}

if (maxSecondary == 0) {
return maxPrimary;
}

double ratio = (double) actualSecondary / (double) actualPrimary;
int resized = maxPrimary;
if (resized * ratio > maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
}

/**
* 获取文件的路径
*
* @param context
* @param uri
* @return
*/
private String getFilePath(Context context, Uri uri) {

String filePath = null;

if (CONTENT.equalsIgnoreCase(uri.getScheme())) {

Cursor cursor = context.getContentResolver().query(uri,
new String[] { MediaStore.Images.Media.DATA }, null, null, null);

if (null == cursor) {
return null;
}

try {
if (cursor.moveToNext()) {
filePath = cursor.getString(cursor
.getColumnIndex(MediaStore.Images.Media.DATA));
}
} finally {
cursor.close();
}
}

// 从文件中选择
if (FILE.equalsIgnoreCase(uri.getScheme())) {
filePath = uri.getPath();
}

return filePath;
}

/**调用工具类压缩图片
*
ImageCompress compress = new ImageCompress();
ImageCompress.CompressOptions options = new ImageCompress.CompressOptions();
options.uri = Uri.fromFile(new File(sourcePath));
options.maxWidth=Constants.RESIZEBITMAP_WIDTH;
options.maxHeight=Constants.RESIZEBITMAP_HEIGHT;
Bitmap bitmap = compress.compressFromUri(UploadWithPhotoBaseActivity.this, options);
*/
}

在前面的GGBA颜色制作特效这篇笔记中,说了图片由像素组成,像素由
色相,饱和度,亮度组成。当图片的像素不变时,把它读取到内存中不是不会节省开销的。

这里有一个压缩质量的方法来压缩图片,要把图片压缩到100k以下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void compressBmpToFile(Bitmap bmp,File file){  
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int options = 80;
bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
while (baos.toByteArray().length / 1024 > 100) {
baos.reset();
options -= 10;
bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
}
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}

如果原来你读取图片时发生内存溢出,然后高兴的去调用这个方法,你会发现,并没什么卵用。
还是照样溢出。
这是为什么呢?这个图片压缩的是质量,图片的长宽没变,然后得像素不变,然后得出不会节省内存的结论。
所以该溢出还是要溢出。
那么是什么让图片质量下降了?官方文档说这是像素组成元素中的亮度被抛弃的缘故。所以质量压缩会让
图变得模糊。

那么怎么才能降低像素呢,那自然是调整长宽。
长宽变小了,像素自然低了。

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
private Bitmap compressImageFromFile(String srcPath) {  
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inJustDecodeBounds = true;//只读边,不读内容 ,这个很重要
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);

newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
float hh = 800f;//
float ww = 480f;//
int be = 1;
if (w > h && w > ww) {
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;//设置采样率

newOpts.inPreferredConfig = Config.ARGB_8888;//该模式是默认的,可不设
newOpts.inPurgeable = true;// 同时设置才会有效
newOpts.inInputShareable = true;//。当系统内存不够时候图片自动被回收

bitmap = BitmapFactory.decodeFile(srcPath, newOpts);

return bitmap;
}

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
package rongshanghui.tastebychance.com.rongshanghui.util;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.util.Log;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
* Created by shenbinghong on 2018/1/12.
*/
public class BitmapUtils {
public static Bitmap toRectBmp(Bitmap srcBitmap, int width, int height) {
return to(srcBitmap, width, height, 10f, 10f);
}

public static Bitmap toCircleBmp(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float roundPx = width <= height ? width / 2 : height / 2;
int l = width <= height ? width : height;

return to(bitmap, l, l, roundPx, roundPx);
}

private static Bitmap to(final Bitmap srcBitmap, final int retWidth,
final int retHeight, final float rx, final float ry) {
int width = srcBitmap.getWidth();
int height = srcBitmap.getHeight();
final Rect src;
final Rect dst = new Rect(0, 0, retWidth, retHeight);

if (width <= height) {
src = new Rect(0, 0, width, width);
} else {
float clip = (width - height) / 2;
src = new Rect((int) clip, 0, (int) (width - clip), height);
}
Bitmap output = Bitmap.createBitmap(retWidth, retHeight,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final RectF rectF = new RectF(dst);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(0xff424242);
canvas.drawRoundRect(rectF, rx, ry, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBitmap, src, new Rect(0, 0, retWidth, retHeight),
paint);
return output;
}

/** * 从SDCard上读取图片 * @param pathName * @return */
public static Bitmap getBitmapFromSDCard(String pathName) {
return BitmapFactory.decodeFile(pathName);
}

/** * 缩放图片 * @param bitmap * @param width * @param height * @return */
public static Bitmap zoomBitmap(Bitmap bitmap, int width, int height) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale((float) width / w, (float) height / h);
return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
}

/** * 将Drawable转化为Bitmap * @param drawable * @return */
public static Bitmap drawableToBitmap(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, drawable
.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);
return bitmap;
}

/** * 获得圆角图片 * @param bitmap * @param roundPx * @return */
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) {
Bitmap output = null;
try {
output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
} catch (Exception e) {
// TODO: handle exception
output = Bitmap.createBitmap(bitmap.getWidth() / 3,
bitmap.getHeight() / 3, Bitmap.Config.ARGB_8888);
}
;
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}

/** * 获得带倒影的图片 * @param bitmap * @return */
public static Bitmap getReflectionImageWithOrigin(Bitmap bitmap) {
final int reflectionGap = 4;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height / 2,
width, height / 2, matrix, false);
Bitmap bitmapWithReflection = Bitmap.createBitmap(width,
(height + height / 2), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmapWithReflection);
canvas.drawBitmap(bitmap, 0, 0, null);
Paint defaultPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint);
canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff,
0x00ffffff, Shader.TileMode.CLAMP);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()
+ reflectionGap, paint);
return bitmapWithReflection;
}

public static byte[] readStream(InputStream inStream) throws Exception {
byte[] buffer = new byte[1024];
int len = -1;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
byte[] data = outStream.toByteArray();
outStream.close();
inStream.close();
return data;
}

public static Bitmap getPicFromBytes(byte[] bytes,
BitmapFactory.Options opts) {
if (bytes != null)
if (opts != null)
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length,
opts);
else
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
return null;
}

public static boolean setBitmapToFile(Bitmap mImg, String path) {
BufferedOutputStream bos = null;
/*File mFile = new File(path);
if (!mFile.exists()){
mFile.mkdirs();
}*/

File mFile = new File(Environment.getExternalStorageDirectory(), "compress.jpg");
if (!mFile.exists()){
try {
mFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}

try {
bos = new BufferedOutputStream(new FileOutputStream(mFile));
if (mImg != null) {
mImg.compress(Bitmap.CompressFormat.JPEG, 100, bos);
}
// finish();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
bos.flush();
bos.close();
mFile = null;
} catch (IOException e) {
e.printStackTrace();
}
}

return false;
}

public static Bitmap getWebImage(String url) {
Log.i("returnBitMap", "url=" + url);
URL myFileUrl = null;
Bitmap bitmap = null;
try {
myFileUrl = new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
HttpURLConnection conn = (HttpURLConnection) myFileUrl
.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}

// public static void getMediaFile(String url, String fileName)
// throws IOException {
// Log.i("getMediaFile", "url=" + url);
// URL myFileUrl = null;
// InputStream is = null;
// HttpURLConnection conn;
// File file = new File(DataBase.CARD_FILE_PATH + fileName + ".amr");
// if (!file.exists()) {
// file.createNewFile();
// }
// try {
// myFileUrl = new URL(url);
// } catch (MalformedURLException e) {
// e.printStackTrace();
// }
// try {
// conn = (HttpURLConnection) myFileUrl.openConnection();
// conn.setDoInput(true);
// conn.connect();
// is = conn.getInputStream();
// } catch (IOException e) {
// e.printStackTrace();
// }
// OutputStream os = new FileOutputStream(file);
// byte[] buffer = new byte[1024 * 2];
// while ((is.read(buffer)) != -1) {
// os.write(buffer);
// }
// if (null != os) {
// os.flush();
// os.close();
// }
// if (null != is) {
// is.close();
// }
//
// }

/**
* resize
*
* @author c.y
* */
public static Bitmap loadResizedBitmap(String filename, int width,
int height, boolean exact) {
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
if (options.outHeight > 0 && options.outWidth > 0) {
options.inJustDecodeBounds = false;
options.inSampleSize = 2;
while (options.outWidth / options.inSampleSize > width
&& options.outHeight / options.inSampleSize > height) {
options.inSampleSize++;
}
bitmap = BitmapFactory.decodeFile(filename, options);
if (bitmap != null && exact) {
bitmap = Bitmap
.createScaledBitmap(bitmap, width, height, false);
}
}
return bitmap;
}

public static byte[] Bitmap2Bytes(Bitmap bm) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
}

自己定义的多种图片缓存(Java六大原则)

自定义图片缓存策略

  • 定义缓存接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ImageCache {    
/**
* 添加缓存
* @param url 缓存的图片url作为缓存的key
* @param bitmap 缓存的bitmap
*/
void put(String url, Bitmap bitmap);
/**
* 获取缓存的图片
* @param url 缓存的图片url
* @return
*/
Bitmap get(String url);
}
  • 内存缓存
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
public class MemoryCache implements ImageCache {
public MemoryCache() {
initImageCache();
}

LruCache<String, Bitmap> memoryCache;
private void initImageCache() {
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);//计算可使用的最大内存
int cacheSize = maxMemory / 4;//只用最大内存的四分之一作为缓存的大小
memoryCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight() / 1024;
}
};
}

@Override
public void put(String url, Bitmap bitmap) {
memoryCache.put(url, bitmap);
}

@Override
public Bitmap get(String url) {
return memoryCache.get(url);
}
}
  • sd卡缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DiskCache implements ImageCache{
public static final String cacheDir = "sdcard/cache/";

@Override
public void put(String url, Bitmap bitmap) {
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(cacheDir + url);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
CloseableUtil.close(outputStream);
}
}

@Override
public Bitmap get(String url) {
return BitmapFactory.decodeFile(cacheDir + url);
}
}
  • 双缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DoubleCache implements ImageCache {
MemoryCache memoryCache = new MemoryCache();//内存缓存
DiskCache diskCache = new DiskCache();//sd卡缓存

@Override
public void put(String url, Bitmap bitmap) {
memoryCache.put(url, bitmap);
diskCache.put(url, bitmap);
}

@Override
public Bitmap get(String url) {
Bitmap bitmap = null;
bitmap = memoryCache.get(url);
if (bitmap == null){
bitmap = diskCache.get(url);
}
return bitmap;
}
}
  • 关闭流的类
1
2
3
4
5
6
7
8
9
10
11
public class CloseableUtil {
public static void close(Closeable closeable){
try {
if (closeable != null){
closeable.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
  • 图片加载
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
public class ImageLoader {

public ImageLoader() {
}

//图片缓存类
ImageCache imageCache = new MemoryCache();

//线程池,线程数量为CPU的数量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

/**
* 设置要使用哪种缓存
* @param imageCache
*/
public void setImageCache(ImageCache imageCache){
this.imageCache = imageCache;
}

/**
* 显示图片
* @param url 图片的url
* @param imageView 要显示的view
*/
public void displayImage(final String url, final ImageView imageView){
Bitmap bitmap = imageCache.get(url);

if (bitmap != null){
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(url);
if (bitmap == null){
return;
}

if (imageView.getTag().equals(url)){
imageView.setImageBitmap(bitmap);
}
imageCache.put(url, bitmap);
}
});



}

/**
* 下载图片
* @param imageUrl 网络的图片地址
* @return 返回bitmap对象
*/
private Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return 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
ImageLoader imageLoader = new ImageLoader();
/**
* 内存缓存方式
*/
imageLoader.setImageCache(new MemoryCache());
/**
* SD卡缓存方式
*/
imageLoader.setImageCache(new DiskCache());
/**
* 双缓存方式
*/
imageLoader.setImageCache(new DoubleCache());
/**
* 自定义缓存方式
*/
imageLoader.setImageCache(new ImageCache() {
@Override
public void put(String url, Bitmap bitmap) {
//用户自定义的缓存方式
}

@Override
public Bitmap get(String url) {
//从缓存中获取图片
return null;
}
});
imageLoader.displayImage(url, imageView);

ImageIO图片工具类(获取路径、保存相册等)

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
package com.u1city.androidframe.common.file.image;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;

import com.u1city.androidframe.common.file.StorageFileManager;
import com.u1city.androidframe.common.text.StringUtils;
import com.u1city.androidframe.framework.model.file.FileOptions;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;

/**
* 图片存取工具
* Created by zhengjb on 2017/1/13.
*/

public class ImageIO {

private static final String TAG = "ImageIO";

private static final int NO_LIMIT_W = -1;
private static final int NO_LIMIT_H = -1;

/**
* 获取SD卡中最新图片路径
*/
public static String getLatestImage(Context context) {
String latestImage = null;
String[] items = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA};
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, items, null, null, MediaStore.Images.Media._ID + " desc");

if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
latestImage = cursor.getString(1);
break;
}
cursor.close();
}

return latestImage;
}

/**
* 从文件中获取图片,调用者可以指定一个期望的宽高以节省所需内存
*
* @param file 目标文件
* @return bitmap
*/
public static Bitmap readImageFromFile(File file, BitmapFactory.Options options, int desireWidth, int desireHeight) {
if (options == null) {
options = new BitmapFactory.Options();
}

options.inJustDecodeBounds = true;
int acWidth, acHeight;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
acWidth = options.outWidth;
acHeight = options.outHeight;
boolean needScale = true;
boolean fitWidth = false;

//期望的高度,<=0时不做限制
if (desireHeight <= 0) {
fitWidth = true;
}

//期望的宽度,<=0时不做限制
if (desireWidth <= 0) {
fitWidth = false;
}
if (desireHeight <= 0 && desireWidth <= 0) {
needScale = false;
}
if (desireHeight > 0 && desireWidth > 0) {
fitWidth = desireWidth / desireHeight < 1;
}
if (needScale) {
if (fitWidth) {
if (acWidth <= desireWidth) {
needScale = false;
}
} else {
if (acHeight <= desireHeight) {
needScale = false;
}
}
}
if (needScale) {
int scale = 1;
if (fitWidth) {
while (acWidth > desireWidth) {
scale *= 2;
acWidth = acWidth / scale;
}
} else {
while (acHeight > desireHeight) {
scale *= 2;
acHeight = acHeight / scale;
}
}
options.inSampleSize = scale;
}
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
}

/**
* 把图片bitmap写到file文件中
*
* @param bitmap 将要写入的文件
* @param file 目标文件
* @return true操作成功,false操作失败
*/
public static boolean writeImage2File(Bitmap bitmap, File file) {
try {
FileOutputStream fos = new FileOutputStream(file);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] bytes = stream.toByteArray();
fos.write(bytes);
fos.close();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

/**
* 保存图图片
*
* @param context 上下文
* @param sour 要保存的图片
* @param fileName 保存文件的名称
* @throws IOException 创建文件失败时抛出“创建文件失败异常”
*/
public static File saveImage(Context context, Bitmap sour, String fileName) throws IOException {
File f = obtainImageFile(context, fileName);
if (f == null) {
throw new IOException("创建文件失败");
}
writeImage2File(sour, f);
return f;
}

/**
* 获得保存图片的文件
*
* @param context 上下文
* @param fileName 文件名称
* @return 返回获得的文件f
*/
public static File obtainImageFile(Context context, String fileName) {
FileOptions op = new FileOptions();
op.setFileType(FileOptions.FILE_TYPE_EXT_IMAGE);
op.setFileName(fileName);
return StorageFileManager.findFile(context, op);
}

/**
* 获得默认文件名的图片文件
*/
public static File obtainImageFile(Context context) {
return obtainImageFile(context, generateDefaultFileName());
}

/**
* 生成默认的文件名称
*/
public static String generateDefaultFileName() {
Calendar c = Calendar.getInstance();
StringBuffer fname;
fname = new StringBuffer();
fname.append(c.get(Calendar.YEAR)).
append(c.get(Calendar.MONTH) + 1).
append(c.get(Calendar.DAY_OF_MONTH)).
append(c.get(Calendar.HOUR_OF_DAY)).
append(c.get(Calendar.MINUTE)).
append(c.get(Calendar.SECOND));
return fname.toString();
}

/**
* 根据uri获取图片的绝对路径
*
* @param context 上下文
* @param uri 目标uri
* @return 图片的绝对路径
*/
public static String getImagePathFromUri(Context context, Uri uri) {
String imagePath = "";
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, proj, // Which columns to
// return
null, // WHERE clause; which rows to return (all rows)
null, // WHERE clause selection arguments (none)
null); // Order-by clause (ascending by name)

if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (cursor.getCount() > 0 && cursor.moveToFirst()) {
imagePath = cursor.getString(column_index);
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return imagePath;
}

public static Bitmap loadImgThumbnail(Context context, String imgName) {
Bitmap bitmap = null;
Cursor cursor = null;
String[] proj = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME};
try {
cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj, MediaStore.Images.Media.DISPLAY_NAME + "='" + imgName + "'", null, null);
if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
}
} finally {
cursor.close();
}
return bitmap;
}

/**
* 从sour指定的文件中加载图片
*
* @param context 上下文
* @param sour 目标文件
* @param fitScreen true则图片宽高将适应屏幕宽高,在加载大图时可以节省内存
* @return 从sour加载到的图片
*/
public static Bitmap loadBitmapFromFile(Context context, File sour, boolean fitScreen) {
int dirWidth, dirHeight;
if (fitScreen) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
dirWidth = dm.widthPixels;
dirHeight = dm.heightPixels;
} else {
dirWidth = NO_LIMIT_W;
dirHeight = NO_LIMIT_H;
}

return readImageFromFile(sour, null, dirWidth, dirHeight);
}

/**
* 把bitmap保存到file指定的文件中
*
* @param file 保存bitmap的文件
* @param bitmap 将要保存的图片
* @return 保存bitmap的文件
*/
public static File saveBitmap(File file, Bitmap bitmap) {
if (file == null) {
Log.w(TAG, "请传入有效的文件");
return null;
}
writeImage2File(bitmap, file);
return file;
}

public static Bitmap readBitmap(File file) {
return readImageFromFile(file, null, -1, -1);
}

/**
* 保存图片至图片库
*/
public static void saveImageToGallery(Context context, Bitmap bmp, String filePackageName, String fileName) {
//当fileName为null时则自动生成文件名
if (StringUtils.isEmpty(fileName)) {
fileName = generateDefaultFileName() + ".jpg";
}
// 首先保存图片
File appDir = new File(Environment.getExternalStorageDirectory(), filePackageName);
if (!appDir.exists()) {
appDir.mkdir();
}
//String fileName = "wxcode.jpg";
File file = new File(appDir, fileName);
try {
saveBitmap(file, bmp);
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
if (file != null) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context, context.getPackageName() + ".updateProvider", file);
} else {
uri = Uri.fromFile(file);
}
intent.setData(uri);
}
context.sendBroadcast(intent);//这个广播的目的就是更新图库
} catch (Exception e) {
e.printStackTrace();
}
}

public static void saveImageToGallery(Context context, Bitmap sour) {
try {
File f = saveImage(context, sour, generateDefaultFileName());
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
if (f != null) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context, context.getPackageName() + ".updateProvider", f);
} else {
uri = Uri.fromFile(f);
}
intent.setData(uri);
}
context.sendBroadcast(intent);//这个广播的目的就是更新图库
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 是否成功保存到系统相册
* @param context
* @param bmp
* @param filePackageName
* @param fileName
* @return
*/
public static boolean isSaveImageToGallerySuc(Context context, Bitmap bmp, String filePackageName, String fileName) {
//当fileName为null时则自动生成文件名
if (StringUtils.isEmpty(fileName)) {
fileName = generateDefaultFileName() + ".jpg";
}
// 首先保存图片
File appDir = new File(Environment.getExternalStorageDirectory(), filePackageName);
if (!appDir.exists()) {
appDir.mkdir();
}
//String fileName = "wxcode.jpg";
File file = new File(appDir, fileName);
try {
saveBitmap(file, bmp);
insertPicToGallery(context, file);
//删除临时保存的图片
deleteImg(context, file);
return true;
} catch (Exception e) {
e.printStackTrace();
}

return false;
}


public static void deleteImg(Context context, File file){
if(!TextUtils.isEmpty(file.getAbsolutePath())){
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = context.getContentResolver();
String where = MediaStore.Images.Media.DATA + "='" + file.getAbsolutePath() + "'";
//删除图片
mContentResolver.delete(mImageUri, where, null);
}


if (file.exists()) { // 判断文件是否存在
if (file.isFile()) { // 判断是否是文件
file.delete(); // delete()方法 你应该知道 是删除的意思;
} else if (file.isDirectory()) { // 否则如果它是一个目录
File files[] = file.listFiles(); // 声明目录下所有的文件 files[];
for (int i = 0; i < files.length; i++) { // 遍历目录下所有的文件
context.deleteFile(String.valueOf(files[i])); // 把每个文件 用这个方法进行迭代
}
}
file.delete();
} else {
Log.e("删除文件","文件不存在!"+"\n");
}
}

/**
* 已经保存的图片插入到相册内
* @param context
* @param file
*/
public static void insertPicToGallery(Context context, File file){

try {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
if (file != null) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getPath(), file.getName(), "description");
uri = Uri.parse("file://"+ Environment.getExternalStorageDirectory());
} else {
uri = Uri.fromFile(file);
}
intent.setData(uri);
}

context.sendBroadcast(intent);//这个广播的目的就是更新图库
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* Gets the content:// URI from the given corresponding path to a file
* @param context
* @param imageFile
* @return content Uri
*/
public static Uri getImageContentUri(Context context, java.io.File imageFile) {
String filePath = imageFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.Images.Media._ID },
MediaStore.Images.Media.DATA + "=? ",
new String[] { filePath }, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor
.getColumnIndex(MediaStore.MediaColumns._ID));
Uri baseUri = Uri.parse("content://media/external/images/media");
return Uri.withAppendedPath(baseUri, "" + id);
} else {
if (imageFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, filePath);
return context.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
}
}

Pic2Ascii图片转ascii码

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
package me.veryyoung;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* Created by veryyoung on 2015/9/28.
* 类功能:将图片转换为 ascii 码
* 使用:运行 Pic2Ascii.java 的 main 方法,并传入图片的有效路径。
*/
public class Pic2Ascii {

/**
* Some empirically chosen characters that give good results.
*/
private static final char[] defaultCharacters = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
.toCharArray();

public static void main(String[] args) {
if (0 == args.length || args[0].equals("")) {
System.err.println("请输入正确的图片路径");
return;
}
System.out.println(transform(args[0]));

}

public static String transform(String path) {
BufferedImage image = getImage(path);

StringBuffer stringBuffer = new StringBuffer();

for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int index = (int) (Math.random() * defaultCharacters.length);
stringBuffer.append(isBlack(image.getRGB(x, y)) ? " " : defaultCharacters[index]);
stringBuffer.append(isBlack(image.getRGB(x, y)) ? " " : defaultCharacters[index]);
}
stringBuffer.append("\n\r");
}
return stringBuffer.toString();

}


public static boolean isBlack(int pixel) {
boolean result = false;

int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = (pixel) & 0xff;


int tmp = r * r + g * g + b * b;
if (tmp > 3 * 128 * 128) {
result = true;
}

return result;
}

public static BufferedImage getImage(String path) {
BufferedImage image = null;
try {
image = ImageIO.read(new File(path));
} catch (IOException e) {
System.err.println("未能获取到图片");
e.printStackTrace();
}

return image;
}
}

截图工具类

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
package com.u1city.androidframe.common.image.screenshot;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.WebView;

import static com.u1city.androidframe.common.display.DimensUtil.getDisplayHeight;
import static com.u1city.androidframe.common.display.DimensUtil.getDisplayWidth;

/**屏幕截图
* create by shenbh on 2018/6/19 ,on Mac.
*/
public class ScreenShotUtils {

/**
* 获取当前屏幕截图,包含状态栏
* @param activity
* @return
*/
public static Bitmap snapShotWithStatusBar(Activity activity) {
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap= view.getDrawingCache();
int width = getDisplayWidth(activity);//获取屏幕的宽
int height = getDisplayHeight(activity);//获取屏幕的高
Bitmap bm = null;
bm = Bitmap.createBitmap(bitmap, 0, 0, width, height);
view.destroyDrawingCache();
return bm;

}

/**
* 获取当前屏幕截图,不包含状态栏
* @param activity
* @return
*/
public static Bitmap snapShotWithoutStatusBar(Activity activity) {
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap= view.getDrawingCache();
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;//获取状态栏的高
int width = getDisplayWidth(activity);//获取屏幕的宽
int height = getDisplayHeight(activity);//获取屏幕的高
Bitmap bm = Bitmap.createBitmap(bitmap, 0, statusBarHeight, width, height - statusBarHeight);
view.destroyDrawingCache();
return bm;

}

/**
* 获取当前屏幕截图,不包含状态栏以及标题栏(ActionBar)
* @param activity
* @return
*/
public static Bitmap snapShotWithoutStatusBarAndTitle(Activity activity) {
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap= view.getDrawingCache();
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;//获取状态栏的高
int titleBarHeight = activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();//获取标题栏的高
int width = getDisplayWidth(activity);//获取屏幕的宽
int height = getDisplayHeight(activity);//获取屏幕的高
int totalBarHeight=titleBarHeight+statusBarHeight;
Bitmap bm= Bitmap.createBitmap(bitmap, 0, totalBarHeight , width, height - totalBarHeight);
view.destroyDrawingCache();
return bm;
}

/**
* 截取Scrollview等ViewGroup里面的内容,常用于生成长微博,效果可参照简书的生成图片分享
* @param view
* @return
*/
public static Bitmap snapShotForViewGroup(ViewGroup view) {
int totalHeight = 0;
// 获取ViewGroup实际高度
for (int i = 0; i < view.getChildCount(); i++) {
totalHeight += view.getChildAt(i).getHeight();
//view.getChildAt(i).setBackgroundColor(0xffffffff);//这里可以设置背景颜色,以免背景透明看不出效果
}
Bitmap bitmap= Bitmap.createBitmap(view.getWidth(), totalHeight,
Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}


/**截取WebView里面的全面内容
*
* Android 5.0以上 在Activity的setContentView之前要进行如下处理
* if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
* WebView.enableSlowWholeDocumentDraw();
* }
* @param view
* @return
*/
public static Bitmap snapShotForWebView(WebView view) {
float scale = view.getScale(); //获取webview缩放率
int webViewHeight = (int) (view.getContentHeight() * scale);//得到缩放后webview内容的高度
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),webViewHeight,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
}

指定View的截图

截图

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.LruCache;
import android.view.Display;
import android.view.View;
import android.webkit.WebView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;

import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

/**
* <pre>
* desc : 截图
* author : shenbh
* e-mail : shenbh@qq.com
* time : 2022-02-16 17:37
* </pre>
*/
public class ImageCapture {

/**
* 当前页面截图(截取整个屏幕)
* 截取当前窗体的截图,根据[isShowStatusBar]判断是否包含当前窗体的状态栏
* 原理是获取当前窗体decorView的缓存生成图片
*/
public Bitmap captureWindow(Activity activity, Boolean isShowStatusBar) {
// 获取当前窗体的View对象
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
// 生成缓存
view.buildDrawingCache();

Bitmap bitmap = null;
if (isShowStatusBar) {
// 绘制整个窗体,包括状态栏
bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
} else {
// 获取状态栏高度
Rect rect = new Rect();
view.getWindowVisibleDisplayFrame(rect);
Display display = activity.getWindowManager().getDefaultDisplay();

// 减去状态栏高度
bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, rect.top, display.getWidth(), display.getHeight() - rect.top);
}

view.setDrawingCacheEnabled(false);
view.destroyDrawingCache();

return bitmap;
}

/**
* 截取常用的View(TextView,RelativeLayout...)
*
* View已经在界面上展示了,可以直接获取View的缓存
* 对View进行量测,布局后生成View的缓存
* View为固定大小的View,包括TextView,ImageView,LinearLayout,FrameLayout,RelativeLayout等
* @param view 截取的View,View必须有固定的大小,不然drawingCache返回null
* @return 生成的Bitmap
*/
public Bitmap captureView(View view) {
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
// 重新测量一遍View的宽高
view.measure(View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(view.getHeight(), View.MeasureSpec.EXACTLY));
// 确定View的位置
view.layout((int)view.getX(), (int)view.getY(), (int)view.getX() + view.getMeasuredWidth(),
(int)view.getY() + view.getMeasuredHeight());
// 生成View宽高一样的Bitmap
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(),
view.getMeasuredHeight());
view.setDrawingCacheEnabled(false);
view.destroyDrawingCache();
return bitmap;
}

/**
* 截取ScrollerView
* 原理是获取scrollView的子View的高度,然后创建一个子View宽高的画布,将ScrollView绘制在画布上
* @param scrollView 控件
* @return 返回截图后的Bitmap
*/
public Bitmap captureScrollView(ScrollView scrollView) {
int h = 0;
for (int i = 0; i < scrollView.getChildCount(); i++) {
View childView = scrollView.getChildAt(i);
// 获取子View的高度
h += childView.getHeight();
// 设置背景颜色,避免布局里未设置背景颜色,截的图背景黑色
childView.setBackgroundColor(Color.parseColor("#FFFFFF"));
}

Bitmap bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// 将ScrollView绘制在画布上
scrollView.draw(canvas);
return bitmap;
}

/**
* 截取ListView
* 原理:获取到每一个子View,将子View生成的bitmap存入集合,并且累积ListView高度
* 遍历完成后,创建一个ListView大小的画布,将集合的Bitmap绘制到画布上
* @param listView 截图控件对象
* @return 生成的截图对象
*/
public Bitmap captureListView(ListView listView) {
ListAdapter adapter = listView.getAdapter();
int itemCount = adapter.getCount();
int allitemsheight = 0;
ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();

for (int i = 0; i < itemCount; i++) {
// 获取每一个子View
View childView = adapter.getView(i, null, listView);
// 测量宽高
childView.measure(
View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

// 布局位置
childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
// 设置背景颜色,避免是黑色的
childView.setBackgroundColor(Color.parseColor("#FFFFFF"));
childView.setDrawingCacheEnabled(true);
// 生成缓存
childView.buildDrawingCache();
// 将每一个View的截图加入集合
bitmaps.add(childView.getDrawingCache());
// 叠加截图高度
allitemsheight += childView.getMeasuredHeight();
}

// 创建和ListView宽高一样的画布
Bitmap bitmap = Bitmap.createBitmap(listView.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);

Paint paint = new Paint();
float iHeight = 0f;

for (int i = 0; i < bitmaps.size(); i++) {
Bitmap bmp = bitmaps.get(i);
// 将每一个生成的bitmap绘制在画布上
canvas.drawBitmap(bmp, 0f, iHeight, paint);
iHeight += bmp.getHeight();

bmp.recycle();
}
return bitmap;
}

/**
* 截取RecyclerView
* 原理和ListView集合是一样的,获取到每一个Holder的截图放入集合,最后统一绘制到Bitmap上
* @param recyclerView&emsp;要截图的控件
* @return 生成的截图
*/
public Bitmap captureRecyclerView(RecyclerView recyclerView) {
RecyclerView.Adapter adapter = recyclerView.getAdapter();
Bitmap bigBitmap = null;
if (adapter != null) {
int size = adapter.getItemCount();
int height = 0;
Paint paint = new Paint();
int iHeight = 0;
int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);

// Use 1/8th of the available memory for this memory cache.
int cacheSize = maxMemory / 8;
LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize);
for (int i = 0; i < size; i++) {
RecyclerView.ViewHolder holder = adapter.createViewHolder(recyclerView, adapter.getItemViewType(i));
adapter.onBindViewHolder(holder, i);
holder.itemView.measure(
View.MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(),
holder.itemView.getMeasuredHeight());
holder.itemView.setBackgroundColor(Color.parseColor("#FFFFFF"));
holder.itemView.setDrawingCacheEnabled(true);
holder.itemView.buildDrawingCache();
Bitmap drawingCache = holder.itemView.getDrawingCache();
if (drawingCache != null) {
bitmapCache.put(i+"", drawingCache);
}
height += holder.itemView.getMeasuredHeight();
}

bigBitmap = Bitmap.createBitmap(recyclerView.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888);
if (bigBitmap != null) {
Canvas bigCanvas = new Canvas(bigBitmap);
Drawable lBackground = recyclerView.getBackground();
if (lBackground instanceof ColorDrawable){
int lColor = ((ColorDrawable) lBackground).getColor();
bigCanvas.drawColor(lColor);
}

for (int i = 0; i < size; i++) {
Bitmap bitmap = bitmapCache.get(i+"");
bigCanvas.drawBitmap(bitmap, 0f, (float) iHeight, paint);
iHeight += bitmap.getHeight();
bitmap.recycle();
}
}
}
return bigBitmap;
}

/**
* 截取WebView,包含WebView的整个长度
* 在WebView渲染之前要加上以下代码,开启Html缓存,不然会截屏空白
* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
* WebView.enableSlowWholeDocumentDraw()
* }
* WebView的截图很容易遇到内存溢出的问题,因为WebView可以加载很多内容,导致生成的图片特别长,创建Bitmap时容易OOM
*/
public Bitmap captureWebView(WebView webView) {
//&emsp;重新调用WebView的measure方法测量实际View的大小(将测量模式设置为UNSPECIFIED模式也就是需要多大就可以获得多大的空间)
webView.measure(View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
//&emsp;调用layout方法设置布局(使用新测量的大小)
webView.layout(0, 0, webView.getMeasuredWidth(), webView.getMeasuredHeight());
//&emsp;开启WebView的缓存(当开启这个开关后下次调用getDrawingCache()方法的时候会把view绘制到一个bitmap上)
webView.setDrawingCacheEnabled(true);
//&emsp;强制绘制缓存(必须在setDrawingCacheEnabled(true)之后才能调用,否者需要手动调用destroyDrawingCache()清楚缓存)
webView.buildDrawingCache();

Bitmap bitmap = Bitmap.createBitmap(webView.getMeasuredWidth(), webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

//&emsp;已picture为背景创建一个画布
Canvas canvas = new Canvas(bitmap); // 画布的宽高和 WebView 的网页保持一致
Paint paint = new Paint();
//&emsp;设置画笔的定点位置,也就是左上角
canvas.drawBitmap(bitmap, 0f, webView.getMeasuredHeight() * 1f, paint);
//&emsp;将WebView绘制在刚才创建的画板上
webView.draw(canvas);
webView.setDrawingCacheEnabled(false);
webView.destroyDrawingCache();
return bitmap;
}
}

图片选择器