在安卓的开发中肯定避免不了对图像的处理,图像的处理最大的问题就是会出现OOM,为了找到一个更好效率更高的图形处理框架,本小白试了几个开源框架,最后剩下这些个人认为比较好用的~~~
导入
这里需要对文件的读写权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
//异步,compress需要用到
compile 'io.reactivex:rxandroid:1.1.0'
//图片剪裁
compile 'com.yalantis:ucrop:2.2.0'
//图片选择
compile 'com.foamtrace:photopicker:1.0'
//glide图片处理
compile 'com.github.bumptech.glide:glide:3.5.2'
//compress压缩
compile 'id.zelory:compressor:1.0.3'
//luban压缩
compile 'top.zibin:Luban:1.0.9'
图片选择
我这里的图片选择使用了photopicker,可选择1张-9张,当然选择那里的布局可以自定义theme,在manifest里设置就可以了
manifest中的设置
<!-- 照片选择 -->
<activity
android:name="com.foamtrace.photopicker.PhotoPickerActivity"
android:configChanges="orientation|screenSize"
android:screenOrientation="portrait"
android:theme="@style/PhotoPickerTheme" />
<activity
android:name="com.foamtrace.photopicker.PhotoPreviewActivity"
android:screenOrientation="portrait"
android:theme="@style/PhotoPickerTheme" />
在平时的项目中,为了使照片选择可以适用多个项目,我把他做成了DialogFragment,这样,在其他项目只要继承对应接口就可以实现这个功能了
主要实现功能的代码如下(设置选择的属性后打开Activity),选择后会调用onACtivityResult
PhotoPickerIntent intent = new PhotoPickerIntent(getActivity());
intent.setSelectModel(SelectModel.MULTI);
intent.setShowCarema(false); // 是否显示拍照, 默认false
intent.setMaxTotal(type); // 最多选择照片数量,默认为9
intent.setSelectedPaths(imagePaths); // 已选中的照片地址, 用于回显选中状态
startActivityForResult(intent, RESULT_LOAD_IMAGE);//打开Activity选择照片
在DEMO中的拍照功能是直接调用了系统原生的拍照功能
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.i("----requestCode----",requestCode+";;"+ UCrop.REQUEST_CROP+";;"+ UCrop.RESULT_ERROR);
if (resultCode == RESULT_OK ) {
if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA || requestCode == RESULT_LOAD_IMAGE) {
//拍照
if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA) {
Uri uri = data.getData();
if (uri == null) {
Bundle bundle = data.getExtras();
Bitmap bitmap = (Bitmap) bundle.get("data");
String path = Environment.getExternalStorageDirectory().getPath() + "/gz/";
try {
File dirFile = new File(path);
if (!dirFile.exists()) {
dirFile.mkdir();
}
File myCaptureFile = new File(path +new Date().getTime()+ "photo.jpeg");
if (!myCaptureFile.exists()){
myCaptureFile.createNewFile();
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
bos.flush();
bos.close();
uri = Uri.fromFile(myCaptureFile);
} catch (IOException e) {
e.printStackTrace();
}
}
Log.i("---DFragmentTakePho---", uri.toString());
uriList.add(uri);
((MyDialogListContract) getActivity()).returnUri(uriList,getTag());
} else
//从相册筛选
if (requestCode == RESULT_LOAD_IMAGE) {
if (data != null) {
imagePaths = data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT);
for (String uri : imagePaths) {
uriList.add(Uri.fromFile(new File(uri)));
}
Log.i("---DFragmentChoose---", imagePaths.toString());
((MyDialogListContract) getActivity()).returnUri(uriList,getTag());
}else{
Log.e("---dialogChoose---","dataNULL");
}
}
dismiss();//选择好照片就更关闭dialog
}
}
}
压缩
对于压缩,个人认为最好的方案是使用jni的压缩,而在这里使用了两个压缩框架compressor和Luban
Compressor
这里使用rxjava异步的方式来进行压缩
Compressor
.getDefault(this)//使用默认的设置
.compressToFileAsObservable(file)//需要压缩的文件
.subscribeOn(Schedulers.io())//压缩时在非主线程中执行
.observeOn(AndroidSchedulers.mainThread())//执行完成后的异步在主线程中执行
.subscribe(new Action1<File>() {//压缩成功是调用
@Override
public void call(File file) {
Glide.with(CompressActivity.this).load(file).into(img_compress);
Log.i("---Compresslength---",getFileSize(file)+"kb");
tv_compress.setText((int)getFileSize(file)+"kb");
}
}, new Action1<Throwable>() {//压缩时出错调用
@Override
public void call(Throwable throwable) {
Log.e("---Compressor---",throwable.getMessage());
}
});
Luban
Luban的也支持rxjava的异步,这里使用了其提供的默认方式
Luban.get(this).load(file)//设置要压缩的文件
.putGear(Luban.THIRD_GEAR)//压缩的档次
.setCompressListener(new OnCompressListener() {//压缩回调
@Override
public void onStart() {//压缩开始
}
@Override
public void onSuccess(File file) {//压缩成功
Glide.with(CompressActivity.this).load(file).into(img_luban);
Log.i("---Lubanlength---",getFileSize(file)+"kb");
tv_luban.setText((int)getFileSize(file)+"kb");
}
@Override
public void onError(Throwable e) {//压缩失败
Log.e("---Luban---",e.getMessage());
}
}).launch();//开始压缩
这两个压缩框架,Luban的效果会更好,压缩效率更接近微信朋友圈图片的压缩,但是在之前的版本,Luban压缩后的图片文件会没有后缀名(这个很坑),当然在1.0.9开始就修复了这个问题,不过之前在项目中使用了Luban压缩出现了奇怪的现象(具体忘了),后来就换成使用Compressor了.
个人认为,Luban在之后的版本应该能做的更好,优化的更好,但是目前的我还是使用Compressor先了
裁剪
裁剪方面,这里选择了UCROP,个人感觉效果挺不错的
manifest设置
<!--照片剪裁-->
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"/>
主要代码(UCROP的布局颜色设置在此处设置,并不是在manifest中设置,裁剪后回调用onActivityResult)
/**
* 剪裁图片
* @param photoUri
*/
public void setCrop(Uri photoUri){
UCrop uCrop = UCrop.of(photoUri, uri);
uCrop.withAspectRatio(1,1);//裁剪比例
UCrop.Options options = new UCrop.Options();
options.setToolbarColor(Color.parseColor("#008CEE"));//状态栏颜色
options.setStatusBarColor(Color.parseColor("#008CEE"));//通知栏颜色
//开始设置
//一共三个参数,分别对应裁剪功能页面的“缩放”,“旋转”,“裁剪”界面,对应的传入NONE,就表示关闭了其手势操作,比如这里我关闭了缩放和旋转界面的手势,只留了裁剪页面的手势操作
options.setAllowedGestures(UCropActivity.NONE, UCropActivity.ALL, UCropActivity.ALL);
//设置是否展示矩形裁剪框
options.setShowCropFrame(true);
//结束设置
uCrop.withOptions(options);
uCrop.start(this);
}
/**
* 剪裁后会返回触发这里
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == UCrop.REQUEST_CROP) {
Uri resultUri;
try {
resultUri = UCrop.getOutput(data);//获取剪裁的结果,在剪裁时点击返回时会再此触发Null...
}catch (NullPointerException e){
return;
}
Glide.with(this).load(resultUri).into(img);
}else
if (resultCode == UCrop.RESULT_ERROR) {//剪裁出错
Log.e("---cropERR---", data.toString());
}
}
结尾吐槽
以上是本人在项目中接触到的安卓图像处理框架,当时一直纠结着使用哪个比较好,后来都试了下,觉得这些还是挺好用的,除此之外,还有Glide加载图片也是棒棒的,在DEMO中也有使用
DEMO:https://github.com/lewis-v/PhotoTest.git