[小白裝逼]Android圖像處理(壓縮+裁剪+圖片選擇)內附DEMO

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

以上內容,純屬個人理解,如有錯誤,請指出糾正,若有雷同,你肯定在做夢~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章