内容:对图片进行压缩,可指定忽略压缩图片大小和压缩后图片保存位置。在此对多图、图组进行二次封装,使用更方便
Luban原理:使用了Bitmap基础的压缩策略,可见:Bitmap图片压缩、图片副本及特效处理、画板功能(保存相册刷新)、刮刮奖
Luban关键代码:计算压缩比例、设置旋转
源码:
File compress() throws IOException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = computeSize();
Bitmap tagBitmap = BitmapFactory.decodeFile(srcImg, options);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
tagBitmap = rotatingImage(tagBitmap);
tagBitmap.compress(Bitmap.CompressFormat.JPEG, 60, stream);
tagBitmap.recycle();
FileOutputStream fos = new FileOutputStream(tagImg);
fos.write(stream.toByteArray());
fos.flush();
fos.close();
stream.close();
return tagImg;
}
private int computeSize() {
srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;
int longSide = Math.max(srcWidth, srcHeight);
int shortSide = Math.min(srcWidth, srcHeight);
float scale = ((float) shortSide / longSide);
if (scale <= 1 && scale > 0.5625) {
if (longSide < 1664) {
return 1;
} else if (longSide >= 1664 && longSide < 4990) {
return 2;
} else if (longSide > 4990 && longSide < 10240) {
return 4;
} else {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
}
} else if (scale <= 0.5625 && scale > 0.5) {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
} else {
return (int) Math.ceil(longSide / (1280.0 / scale));
}
}
private Bitmap rotatingImage(Bitmap bitmap) {
if (srcExif == null) return bitmap;
Matrix matrix = new Matrix();
int angle = 0;
int orientation = srcExif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
angle = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
angle = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
angle = 270;
break;
}
matrix.postRotate(angle);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
Luban压缩后的效果
好了不废话,直接上如何使用
步骤如下:
1、引入Luban三方库。GitHub:https://github.com/Curzibn/Luban
implementation 'top.zibin:Luban:1.1.8'
2、主代码进行二次封装(未生成工具类,可自行提取,只展示思想)
public class MainActivity extends AppCompatActivity {
private static final int COMPRESS_START = 100;
private static final int COMPRESS_SUCCESS = 101;
private static final int COMPRESS_FAIL = 102;
public static final String SD_PATH = Environment.getExternalStorageDirectory().getPath() + File.separator;
private String path1 = SD_PATH + "test1.jpg";
private String path2 = SD_PATH + "test2.jpg";
private String path3 = SD_PATH + "test3.jpg";
private String path4 = SD_PATH + "test4.jpg";
private String path5 = SD_PATH + "test5.jpg";
private List<List<String>> pathList = new ArrayList<>();
private String cachePath = SD_PATH + "Test";
private int compressChildIndex = 0; //子类压缩指引
private int childTotal = 0; //子类压缩总数
private int compressFatherIndex = 0; //父类压缩指引
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COMPRESS_START:
compressChildIndex = 0;
childTotal = pathList.get(compressFatherIndex).size();
doCompress(pathList.get(compressFatherIndex));
break;
case COMPRESS_SUCCESS:
LogUtil("压缩完毕");
break;
case COMPRESS_FAIL:
LogUtil((String) msg.obj);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
findViewById(R.id.btn_compress).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new PermissionUtil(MainActivity.this, new PermissionUtil.PermissionCheckListener() {
@Override
public void ok() {
compressConfig();
}
@Override
public void notOk() {
Toast.makeText(MainActivity.this, "need permission", Toast.LENGTH_SHORT).show();
}
}, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}).check();
}
});
}
private void initData() {
List<String> paths1 = new ArrayList<>();
List<String> paths2 = new ArrayList<>();
paths1.add(path1);
paths1.add(path2);
paths1.add(path3);
paths2.add(path4);
paths2.add(path5);
pathList.add(paths1);
pathList.add(paths2);
}
private void compressConfig() {
compressFatherIndex = 0;
File cacheFile = new File(cachePath);
if (!cacheFile.exists())
cacheFile.mkdirs();
mHandler.sendEmptyMessage(COMPRESS_START);
}
private void doCompress(List<String> paths) {
Luban.with(this)
.load(paths)
.ignoreBy(100)
.setTargetDir(cachePath)
.setCompressListener(new OnCompressListener() {
@Override
public void onStart() {
}
@Override
public void onSuccess(File file) {
compressChildIndex++; //移动到子类下一张,压缩完成则+1
LogUtil(file.getAbsolutePath() + "=====childTotal:" + childTotal + "=====compressChildIndex:" + compressChildIndex);
//判断上一组是否压缩完毕
if (compressChildIndex >= childTotal) {
compressFatherIndex++; //移动到父类下一组
LogUtil("pathList.size:" + pathList.size() + "=====compressFatherIndex:" + compressFatherIndex);
//判断是否所有组压缩完毕
if (compressFatherIndex >= pathList.size()) {
mHandler.sendEmptyMessage(COMPRESS_SUCCESS);
} else {
mHandler.sendEmptyMessage(COMPRESS_START);
}
}
}
@Override
public void onError(Throwable e) {
Message msg = new Message();
msg.obj = e.toString();
msg.what = COMPRESS_FAIL;
mHandler.sendMessage(msg);
}
}).launch();
}
private void LogUtil(String str) {
Log.e("Luban", str);
}
}
Log打印如下:
com.leixiansheng.lubantest E/Luban: /storage/emulated/0/Test/1567416655160624.jpeg=====childTotal:3=====compressChildIndex:1
com.leixiansheng.lubantest E/Luban: /storage/emulated/0/Test/1567416656157573.jpeg=====childTotal:3=====compressChildIndex:2
com.leixiansheng.lubantest E/Luban: /storage/emulated/0/Test/1567416656813325.jpeg=====childTotal:3=====compressChildIndex:3
com.leixiansheng.lubantest E/Luban: pathList.size:2=====compressFatherIndex:1
com.leixiansheng.lubantest E/Luban: /storage/emulated/0/Test/1567416657053777.jpeg=====childTotal:2=====compressChildIndex:1
com.leixiansheng.lubantest E/Luban: /storage/emulated/0/Test/1567416657730368.jpeg=====childTotal:2=====compressChildIndex:2
com.leixiansheng.lubantest E/Luban: pathList.size:2=====compressFatherIndex:2
com.leixiansheng.lubantest E/Luban: 压缩完毕
记得配置权限!