在安卓的開發中肯定避免不了對圖像的處理,圖像的處理最大的問題就是會出現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