在项目的开发过程中我们需要从相册中选择图片、裁剪、压缩图片等功能,
图片选择
imagPicker图片选择框架
1. 介绍
Android自定义相册,完全仿微信UI,实现了拍照、图片选择(单选/多选)、 裁剪 、旋转、等功能
2. 用法
- 引入
compile 'com.lzy.widget:imagepicker:0.3.2'
- 配置信息
ImagePicker imagePicker = ImagePicker.getInstance();
imagePicker.setImageLoader(new PicassoImageLoader()); //设置图片加载器
imagePicker.setShowCamera(true); //显示拍照按钮
imagePicker.setCrop(true); //允许裁剪(单选才有效)
imagePicker.setSaveRectangle(true); //是否按矩形区域保存
imagePicker.setSelectLimit(9); //选中数量限制
imagePicker.setStyle(CropImageView.Style.RECTANGLE); //裁剪框的形状
imagePicker.setFocusWidth(800); //裁剪框的宽度。单位像素(圆形自动取宽高最小值)
imagePicker.setFocusHeight(800); //裁剪框的高度。单位像素(圆形自动取宽高最小值)
imagePicker.setOutPutX(1000);//保存文件的宽度。单位像素
imagePicker.setOutPutY(1000);//保存文件的高度。单位像素
裁剪(单选才有效)
- 开启相册
Intent intent = new Intent(this, ImageGridActivity.class);
startActivityForResult(intent, IMAGE_PICKER);
- 重写onActivityResult方法,回调结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == ImagePicker.RESULT_CODE_ITEMS) {
if (data != null && requestCode == IMAGE_PICKER) {
ArrayList images = (ArrayList) data.getSerializableExtra(ImagePicker.EXTRA_RESULT_ITEMS);
MyAdapter adapter = new MyAdapter(images);
gridView.setAdapter(adapter);
} else {
Toast.makeText(this, "没有数据", Toast.LENGTH_SHORT).show();
}
}
}
进一步抽取到PandaAndroidLibs库中
public class ImagePickerHelper {
/**
* 选取单张图片
* 默认显示相机
*/
public static void selectSinglePhoto(Activity activity, int requestCode, boolean isCrop) {
selectSinglePhoto(activity, requestCode, isCrop, true);
}
/**
* 选取单张图片
*/
public static void selectSinglePhoto(Activity activity, int requestCode, boolean isCrop, boolean isShowCamera) {
initImagePickerConfig(false, 1, isCrop, isShowCamera);
startImageActivityForResult(activity, requestCode);
}
/**
* 选取多张张图片 (不支持裁剪)
* 默认显示相机
*/
public static void selectMultiPhoto(Activity activity, int requestCode, int num) {
selectMultiPhoto(activity, requestCode, num, true);
}
/**
* 选取多张张图片 (不支持裁剪)
*/
public static void selectMultiPhoto(Activity activity, int requestCode, int num, boolean isShowCamera) {
initImagePickerConfig(false, num, false, isShowCamera);
startImageActivityForResult(activity, requestCode);
}
/**
* 跳转到选择图片界面
*/
private static void startImageActivityForResult(Activity activity, int requestCode) {
Intent intent = new Intent(activity, ImageGridActivity.class);
activity.startActivityForResult(intent, requestCode);
}
/**
* 配置 信息
*/
private static void initImagePickerConfig(boolean isMultiPhoto, int num, boolean isCrop, boolean isShowCamera) {
ImagePicker imagePicker = ImagePicker.getInstance();
imagePicker.setImageLoader(new GlideImageLoader()); //设置图片加载器
imagePicker.setShowCamera(isShowCamera); //显示拍照按钮
imagePicker.setMultiMode(isMultiPhoto);
imagePicker.setCrop(isCrop); //允许裁剪(单选才有效)
imagePicker.setSaveRectangle(true); //是否按矩形区域保存
imagePicker.setSelectLimit(num); //选中数量限制
imagePicker.setStyle(CropImageView.Style.RECTANGLE); //裁剪框的形状
imagePicker.setFocusWidth(800); //裁剪框的宽度。单位像素(圆形自动取宽高最小值)
imagePicker.setFocusHeight(800); //裁剪框的高度。单位像素(圆形自动取宽高最小值)
imagePicker.setOutPutX(1000);//保存文件的宽度。单位像素
imagePicker.setOutPutY(1000);//保存文件的高度。单位像素
}
}
图片的压缩
-
质量压缩 压缩到小于300K
- 缺点
图片失真
耗时
public static void compressImage(Bitmap image, String srcPath) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(CompressFormat.JPEG, 100, baos);
int options = 10;
while(baos.toByteArray().length / 1024 > 300) {
baos.reset();
image.compress(CompressFormat.JPEG, options, baos);
options -= 10;
if(options <= 0) {
break;
}
}
Log.d(TAG, "options = " + (options + 10));
File file = new File(srcPath);
if(file.exists()) {
file.delete();
}
try {
file.createNewFile();
FileOutputStream e = new FileOutputStream(file);
baos.writeTo(e);
e.flush();
baos.close();
e.close();
} catch (IOException var6) {
var6.printStackTrace();
}
}
-
Luban图片压缩
Luban(鲁班) —— Android图片压缩工具,仿微信朋友圈压缩策略。
- 缺点
需要结合rxjava和RxAndroid使用
比较耗时
压缩后有时是没有后缀
- 简单改造
public class Luban {
public static final int FIRST_GEAR = 1;
public static final int THIRD_GEAR = 3;
private static final String TAG = "Luban";
private static String DEFAULT_DISK_CACHE_DIR = "luban_disk_cache";
private static volatile Luban INSTANCE;
private final File mCacheDir;
// private OnCompressListener compressListener;
private File mFile; //原图
private int gear = THIRD_GEAR;
private String filename;
private Luban(File cacheDir) {
mCacheDir = cacheDir;
}
/**
* Returns a directory with a default name in the private cache directory of the application to use to store
* retrieved media and thumbnails.
*
* @param context A context.
* @see #getPhotoCacheDir(Context, String)
*/
private static File getPhotoCacheDir(Context context) {
return getPhotoCacheDir(context, Luban.DEFAULT_DISK_CACHE_DIR);
}
/**
* Returns a directory with the given name in the private cache directory of the application to use to store
* retrieved media and thumbnails.
*
* @param context A context.
* @param cacheName The name of the subdirectory in which to store the cache.
* @see #getPhotoCacheDir(Context)
*/
private static File getPhotoCacheDir(Context context, String cacheName) {
File cacheDir = context.getCacheDir();
if (cacheDir != null) {
File result = new File(cacheDir, cacheName);
if (!result.mkdirs() && (!result.exists() || !result.isDirectory())) {
// File wasn't able to create a directory, or the result exists but not a directory
return null;
}
return result;
}
if (Log.isLoggable(TAG, Log.ERROR)) {
Log.e(TAG, "default disk cache dir is null");
}
return null;
}
public static Luban get(Context context) {
if (INSTANCE == null) INSTANCE = new Luban(Luban.getPhotoCacheDir(context));
return INSTANCE;
}
public File launch() {
checkNotNull(mFile, "the image file cannot be null, please call .load() before this method!");
if (gear == Luban.FIRST_GEAR) {
return firstCompress(mFile);
} else if (gear == Luban.THIRD_GEAR) {
return thirdCompress(mFile);
} else {
return firstCompress(mFile);
}
}
/**
* 三级压缩
*
* @param file
* @return
*/
private File thirdCompress(@NonNull File file) {
String thumb = mCacheDir.getAbsolutePath() + File.separator +
(TextUtils.isEmpty(filename) ? System.currentTimeMillis() + ".png" : filename);
double size;
String filePath = file.getAbsolutePath();
int angle = getImageSpinAngle(filePath);
int width = getImageSize(filePath)[0];
int height = getImageSize(filePath)[1];
int thumbW = width % 2 == 1 ? width + 1 : width;
int thumbH = height % 2 == 1 ? height + 1 : height;
width = thumbW > thumbH ? thumbH : thumbW;
height = thumbW > thumbH ? thumbW : thumbH;
double scale = ((double) width / height);
if (scale <= 1 && scale > 0.5625) {
if (height < 1664) {
if (file.length() / 1024 < 150) return file;
size = (width * height) / Math.pow(1664, 2) * 150;
size = size < 60 ? 60 : size;
} else if (height >= 1664 && height < 4990) {
thumbW = width / 2;
thumbH = height / 2;
size = (thumbW * thumbH) / Math.pow(2495, 2) * 300;
size = size < 60 ? 60 : size;
} else if (height >= 4990 && height < 10240) {
thumbW = width / 4;
thumbH = height / 4;
size = (thumbW * thumbH) / Math.pow(2560, 2) * 300;
size = size < 100 ? 100 : size;
} else {
int multiple = height / 1280 == 0 ? 1 : height / 1280;
thumbW = width / multiple;
thumbH = height / multiple;
size = (thumbW * thumbH) / Math.pow(2560, 2) * 300;
size = size < 100 ? 100 : size;
}
} else if (scale <= 0.5625 && scale > 0.5) {
if (height < 1280 && file.length() / 1024 < 200) return file;
int multiple = height / 1280 == 0 ? 1 : height / 1280;
thumbW = width / multiple;
thumbH = height / multiple;
size = (thumbW * thumbH) / (1440.0 * 2560.0) * 400;
size = size < 100 ? 100 : size;
} else {
int multiple = (int) Math.ceil(height / (1280.0 / scale));
thumbW = width / multiple;
thumbH = height / multiple;
size = ((thumbW * thumbH) / (1280.0 * (1280 / scale))) * 500;
size = size < 100 ? 100 : size;
}
return compress(filePath, thumb, thumbW, thumbH, angle, (long) size);
}
/**
* 一级压缩
*/
private File firstCompress(@NonNull File file) {
int minSize = 60;
int longSide = 720;
int shortSide = 1280;
String filePath = file.getAbsolutePath();
String thumbFilePath = mCacheDir.getAbsolutePath() + File.separator + (TextUtils.isEmpty(filename) ? System.currentTimeMillis()+ ".png" : filename);
long size = 0;
long maxSize = file.length() / 5;
int angle = getImageSpinAngle(filePath);
int[] imgSize = getImageSize(filePath);
int width = 0, height = 0;
if (imgSize[0] <= imgSize[1]) {
double scale = (double) imgSize[0] / (double) imgSize[1];
if (scale <= 1.0 && scale > 0.5625) {
width = imgSize[0] > shortSide ? shortSide : imgSize[0];
height = width * imgSize[1] / imgSize[0];
size = minSize;
} else if (scale <= 0.5625) {
height = imgSize[1] > longSide ? longSide : imgSize[1];
width = height * imgSize[0] / imgSize[1];
size = maxSize;
}
} else {
double scale = (double) imgSize[1] / (double) imgSize[0];
if (scale <= 1.0 && scale > 0.5625) {
height = imgSize[1] > shortSide ? shortSide : imgSize[1];
width = height * imgSize[0] / imgSize[1];
size = minSize;
} else if (scale <= 0.5625) {
width = imgSize[0] > longSide ? longSide : imgSize[0];
height = width * imgSize[1] / imgSize[0];
size = maxSize;
}
}
return compress(filePath, thumbFilePath, width, height, angle, size);
}
/**
* 获取图片的宽、高
* obtain the image's width and height
*
* @param imagePath the path of image
*/
public int[] getImageSize(String imagePath) {
int[] res = new int[2];
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = 1;
BitmapFactory.decodeFile(imagePath, options);
res[0] = options.outWidth;
res[1] = options.outHeight;
return res;
}
/**
* 压缩图片
* obtain the thumbnail that specify the size
*
* @param imagePath the target image path
* @param width the width of thumbnail
* @param height the height of thumbnail
* @return {@link Bitmap}
*/
private Bitmap compress(String imagePath, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
int outH = options.outHeight;
int outW = options.outWidth;
int inSampleSize = 1;
if (outH > height || outW > width) {
int halfH = outH / 2;
int halfW = outW / 2;
while ((halfH / inSampleSize) > height && (halfW / inSampleSize) > width) {
inSampleSize *= 2;
}
}
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
int heightRatio = (int) Math.ceil(options.outHeight / (float) height);
int widthRatio = (int) Math.ceil(options.outWidth / (float) width);
if (heightRatio > 1 || widthRatio > 1) {
if (heightRatio > widthRatio) {
options.inSampleSize = heightRatio;
} else {
options.inSampleSize = widthRatio;
}
}
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imagePath, options);
}
/**
* obtain the image rotation angle
*
* @param path path of target image
*/
private int getImageSpinAngle(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
/**
* 指定参数压缩图片
* create the thumbnail with the true rotate angle
*
* @param largeImagePath the big image path
* @param thumbFilePath the thumbnail path
* @param width width of thumbnail
* @param height height of thumbnail
* @param angle rotation angle of thumbnail
* @param size the file size of image
*/
private File compress(String largeImagePath, String thumbFilePath, int width, int height, int angle, long size) {
Bitmap thbBitmap = compress(largeImagePath, width, height);
thbBitmap = rotatingImage(angle, thbBitmap);
return saveImage(thumbFilePath, thbBitmap, size);
}
/**
* 旋转图片
* rotate the image with specified angle
*
* @param angle the angle will be rotating 旋转的角度
* @param bitmap target image 目标图片
*/
private static Bitmap rotatingImage(int angle, Bitmap bitmap) {
//rotate image
Matrix matrix = new Matrix();
matrix.postRotate(angle);
//create a new image
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
/**
* 保存图片到指定路径
* Save image with specified size
*
* @param filePath the image file save path 储存路径
* @param bitmap the image what be save 目标图片
* @param size the file size of image 期望大小
*/
private File saveImage(String filePath, Bitmap bitmap, long size) {
checkNotNull(bitmap, TAG + "bitmap cannot be null");
File result = new File(filePath.substring(0, filePath.lastIndexOf("/")));
if (!result.exists() && !result.mkdirs()) return null;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
int options = 100;
bitmap.compress(Bitmap.CompressFormat.JPEG, options, stream);
while (stream.toByteArray().length / 1024 > size && options > 6) {
stream.reset();
options -= 6;
bitmap.compress(Bitmap.CompressFormat.JPEG, options, stream);
}
try {
FileOutputStream fos = new FileOutputStream(filePath);
fos.write(stream.toByteArray());
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
return new File(filePath);
}
}
- 使用
File file = Luban.get(context)
.load(new File(imageItem.path))
.putGear(lib.Luban.THIRD_GEAR)
.launch();