本人一直都是很喜欢链式编程的风格的,因为简单、易用,还好看。所以闲来无事就整合了一下ImageUtil 图片操作工具类,提取了获取bitmap,保存图片,尺寸压缩,质量压缩等几个功能,利用建造者模式(还不敢确定是不是)写了这个ImageLite工具类,使用方式在下面:
/**
* @desc
* @auth 方毅超
* @time 2018/1/4 10:42
*/
public class ImageLite {
static private int PLAN = 0;
static private RatioAttribute ratioAttribute;
static private CompressAttribute compressAttribute;
static {
ratioAttribute = new RatioAttribute();
compressAttribute = new CompressAttribute();
}
/**
* Get bitmap from specified image path
*
* @param imgPath
* @return
*/
public static Bitmap getBitmap(String imgPath) {
// Get bitmap through image path
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 防止OOM发生
newOpts.inJustDecodeBounds = false;
// Do not compress
newOpts.inSampleSize = 1;
newOpts.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(imgPath, newOpts);
}
/**
* Store bitmap into specified image path
*
* @param bitmap
* @param outPath
* @throws FileNotFoundException
*/
public static void storeImage(Bitmap bitmap, String outPath) throws FileNotFoundException {
try {
FileOutputStream os = new FileOutputStream(outPath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 尺寸压缩,直接压缩位图
* 实际上只是设置参数,只有调用start()才会执行动作
*
* @param image
* @return
*/
public static RatioAttribute ratio(Bitmap image) {
PLAN = RatioAttribute.RATIO_BY_BITMAP;
ratioAttribute.image = image;
return ratioAttribute;
}
/**
* 尺寸压缩,通过图片路径
* 实际上只是设置参数,只有调用start()才会执行动作
*
* @param imgPath image path
* @return
*/
public static RatioAttribute ratio(String imgPath) {
PLAN = RatioAttribute.RATIO_BY_STR;
ratioAttribute.imgPath = imgPath;
return ratioAttribute;
}
/**
* 质量压缩,直接压缩位图,并生成压缩图
* 实际上只是设置参数,只有调用start()才会执行动作
* Compress by quality, and generate image to the path specified
*
* @param bitmap
* @throws IOException
*/
public static CompressAttribute compress(Bitmap bitmap) {
PLAN = CompressAttribute.COMPRESS_BY_BITMAP;
compressAttribute.image = bitmap;
return compressAttribute;
}
/**
* 质量压缩,通过图片路径,并生成压缩图
* 实际上只是设置参数,只有调用start()才会执行动作
* Compress by quality, and generate image to the path specified
*
* @param imgPath
* @param needsDelete Whether delete original file after compress
* @throws IOException
*/
public static CompressAttribute compress(String imgPath, boolean needsDelete) {
PLAN = CompressAttribute.COMPRESS_BY_STR;
compressAttribute.imgPath = imgPath;
compressAttribute.needsDelete = needsDelete;
return compressAttribute;
}
/**
* 尺寸压缩真正的属性、操作类
*/
public static class RatioAttribute {
static private final int RATIO_BY_BITMAP = 1;
static private final int RATIO_BY_STR = 2;
float pixelW = 120f;//需要压缩成的宽度,默认120像素
float pixelH = 240f;//需要压缩成的高度,默认240像素
Bitmap image;//需要被压缩的位图
String imgPath;//需要被压缩图片的路径
String outPath;//压缩完之后保存的路径
public RatioAttribute setPixel(float w, float h) {
pixelW = w;
pixelH = h;
return ratioAttribute;
}
public RatioAttribute setOutPath(String path) {
outPath = path;
return ratioAttribute;
}
/**
* 尺寸压缩,直接压缩位图
* Compress image by size, this will modify image width/height.
* Used to get thumbnail
*
* @param image
* @return
*/
private Bitmap doRatioAndGenThumbByBitmap(Bitmap image) throws FileNotFoundException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, os);
if (os.toByteArray().length / 1024 > 1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
os.reset();//重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;
newOpts.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
float hh = pixelH;// 设置高度为240f时,可以明显看到图片缩小了
float ww = pixelW;// 设置宽度为120f,可以明显看到图片缩小了
//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//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;//设置缩放比例
//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
is = new ByteArrayInputStream(os.toByteArray());
bitmap = BitmapFactory.decodeStream(is, null, newOpts);
//压缩好比例大小后再进行质量压缩
// return compress(bitmap, maxSize); // 这里再进行质量压缩的意义不大,反而耗资源,删除
// Generate compressed image file
if (outPath != null && !outPath.isEmpty()) {
storeImage(bitmap, outPath);
outPath = null;//使用完立即置空
}
return bitmap;
}
/**
* 尺寸压缩,通过图片路径
* Compress image by pixel, this will modify image width/height.
* Used to get thumbnail
*
* @param imgPath image path
* @return
*/
private Bitmap doRatioAndGenThumbByStr(String imgPath) throws FileNotFoundException {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容
newOpts.inJustDecodeBounds = true;
newOpts.inPreferredConfig = Bitmap.Config.RGB_565;
// Get bitmap info, but notice that bitmap is null now
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 想要缩放的目标尺寸
float hh = ratioAttribute.pixelH;// 设置高度为240f时,可以明显看到图片缩小了
float ww = ratioAttribute.pixelW;// 设置宽度为120f,可以明显看到图片缩小了
// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//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;//设置缩放比例
// 开始压缩图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
// 压缩好比例大小后再进行质量压缩
// return compress(bitmap, maxSize); // 这里再进行质量压缩的意义不大,反而耗资源,删除
// Generate compressed image file
if (outPath != null && !outPath.isEmpty()) {
storeImage(bitmap, outPath);
outPath = null;//使用完立即置空
}
return bitmap;
}
public Bitmap start() {
Bitmap bitmap = null;
try {
switch (PLAN) {
case RATIO_BY_BITMAP:
bitmap = doRatioAndGenThumbByBitmap(image);
break;
case RATIO_BY_STR:
bitmap = doRatioAndGenThumbByStr(imgPath);
break;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return bitmap;
}
}
/**
* 质量压缩真正的属性、操作类
*/
public static class CompressAttribute {
static private final int COMPRESS_BY_BITMAP = 3;
static private final int COMPRESS_BY_STR = 4;
Bitmap image;//需要被压缩的位图
String imgPath;//需要被压缩图片的路径
int maxSize = 100;//需要被压缩成的大小,默认100k
boolean needsDelete;//用于判断压缩完之后是否删除原路径下的原图片
String outPath;//压缩完之后保存的路径
public CompressAttribute setMaxSize(int maxSize) {
this.maxSize = maxSize;
return compressAttribute;
}
public CompressAttribute setOutPath(String path) {
outPath = path;
return compressAttribute;
}
/**
* 质量压缩,直接压缩位图,并生成压缩图
* Compress by quality, and generate image to the path specified
*
* @param bitmap
* @param maxSize target will be compressed to be smaller than this size.(kb)
* @throws IOException
*/
private Bitmap doCompressAndGenImageByBitmap(Bitmap bitmap, int maxSize) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
// scale
int options = 100;
// Store the bitmap into output stream(no compress)
bitmap.compress(Bitmap.CompressFormat.JPEG, options, os);
// Compress by loop
while (os.toByteArray().length / 1024 > maxSize) {
// Clean up os
os.reset();
// interval 10
options -= 10;
bitmap.compress(Bitmap.CompressFormat.JPEG, options, os);
}
// Generate compressed image file
if (outPath != null && !outPath.isEmpty()) {
FileOutputStream fos = new FileOutputStream(outPath);
fos.write(os.toByteArray());
fos.flush();
fos.close();
outPath = null;//使用完立即置空
}
ByteArrayInputStream isBm = new ByteArrayInputStream(os.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
return BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
}
/**
* 质量压缩,通过图片路径,并生成压缩图
* Compress by quality, and generate image to the path specified
*
* @param imgPath
* @param maxSize target will be compressed to be smaller than this size.(kb)
* @param needsDelete Whether delete original file after compress
* @throws IOException
*/
private Bitmap doCompressAndGenImageByStr(String imgPath, int maxSize, boolean needsDelete) throws IOException {
Bitmap bitmap = doCompressAndGenImageByBitmap(getBitmap(imgPath), maxSize);
// Delete original file
if (needsDelete) {
File file = new File(imgPath);
if (file.exists()) {
file.delete();
}
}
return bitmap;
}
public Bitmap start(){
Bitmap bitmap = null;
try {
switch (PLAN) {
case COMPRESS_BY_BITMAP:
bitmap = doCompressAndGenImageByBitmap(image, maxSize);
break;
case COMPRESS_BY_STR:
bitmap = doCompressAndGenImageByStr(imgPath, maxSize, needsDelete);
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
}
使用方式:是不是很清新很明了。
ImageLite.ratio(qrcodeBitmap)
.setPixel(100f, 100f)
.setOutPath(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Csair")
.start();
ImageLite.compress(qrcodeBitmap)
.setMaxSize(100)
.setOutPath(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Csair")
.start();
好吧!其实有点多此一举了,就这么几个功能完全没有必要写成这样,但是重点是要了解这种设计模式,不是吗?保不齐以后碰到某些功能类、模块,就得这样写呢!