uCrop圖片裁剪

2018-03-02 08:28:52來源:cnblogs.com作者:ganchuanpu人點擊

分享

uCrop使用

github地址

https://github.com/Yalantis/uCrop
然后clone或下載到本地,運行之。

效果預覽

app/build.gradle

compile 'com.yalantis:ucrop:1.5.0'

AndroidManifest.xml

1 <activity2     android:name="com.yalantis.ucrop.UCropActivity"3     android:screenOrientation="portrait"4     android:theme="@style/Theme.AppCompat.Light.NoActionBar" />

這里theme可以改成自己的

配置uCrop

 1  /** 2   * 啟動裁剪 3   * @param activity 上下文 4   * @param sourceFilePath 需要裁剪圖片的絕對路徑 5   * @param requestCode 比如:UCrop.REQUEST_CROP 6   * @param aspectRatioX 裁剪圖片寬高比 7   * @param aspectRatioY 裁剪圖片寬高比 8   * @return 9   */10 public static String startUCrop(Activity activity, String sourceFilePath, 11     int requestCode, float aspectRatioX, float aspectRatioY) {12     Uri sourceUri = Uri.fromFile(new File(sourceFilePath));13     File outDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);14     if (!outDir.exists()) {15         outDir.mkdirs();16     }17     File outFile = new File(outDir, System.currentTimeMillis() + ".jpg");18     //裁剪后圖片的絕對路徑19     String cameraScalePath = outFile.getAbsolutePath();20     Uri destinationUri = Uri.fromFile(outFile);21     //初始化,第一個參數:需要裁剪的圖片;第二個參數:裁剪后圖片22     UCrop uCrop = UCrop.of(sourceUri, destinationUri);23     //初始化UCrop配置24     UCrop.Options options = new UCrop.Options();25     //設置裁剪圖片可操作的手勢26     options.setAllowedGestures(UCropActivity.SCALE, UCropActivity.ROTATE, UCropActivity.ALL);27     //是否隱藏底部容器,默認顯示28     options.setHideBottomControls(true);29     //設置toolbar顏色30     options.setToolbarColor(ActivityCompat.getColor(activity, R.color.colorPrimary));31     //設置狀態欄顏色32     options.setStatusBarColor(ActivityCompat.getColor(activity, R.color.colorPrimary));33     //是否能調整裁剪框34     options.setFreeStyleCropEnabled(true);35     //UCrop配置36     uCrop.withOptions(options);37     //設置裁剪圖片的寬高比,比如16:938     uCrop.withAspectRatio(aspectRatioX, aspectRatioY);39     //uCrop.useSourceImageAspectRatio();40     //跳轉裁剪頁面41     uCrop.start(activity, requestCode);42     return cameraScalePath;43 }

其他配置

 1 //設置Toolbar標題 2 void setToolbarTitle(@Nullable String text) 3 //設置裁剪的圖片格式 4 void setCompressionFormat(@NonNull Bitmap.CompressFormat format) 5 //設置裁剪的圖片質量,取值0-100 6 void setCompressionQuality(@IntRange(from = 0) int compressQuality) 7 //設置最多縮放的比例尺 8 void setMaxScaleMultiplier(@FloatRange(from = 1.0, fromInclusive = false) float maxScaleMultiplier) 9 //動畫時間10 void setImageToCropBoundsAnimDuration(@IntRange(from = 100) int durationMillis)11 //設置圖片壓縮最大值12 void setMaxBitmapSize(@IntRange(from = 100) int maxBitmapSize)13 //是否顯示橢圓裁剪框陰影14 void setOvalDimmedLayer(boolean isOval) 15 //設置橢圓裁剪框陰影顏色16 void setDimmedLayerColor(@ColorInt int color)17 //是否顯示裁剪框18 void setShowCropFrame(boolean show)19 //設置裁剪框邊的寬度20 void setCropFrameStrokeWidth(@IntRange(from = 0) int width)21 //是否顯示裁剪框網格22 void setShowCropGrid(boolean show) 23 //設置裁剪框網格顏色24 void setCropGridColor(@ColorInt int color)25 //設置裁剪框網格寬26 void setCropGridStrokeWidth(@IntRange(from = 0) int width)

onActivityResult

經過裁剪,返回結果,這里我一般只需要裁剪后的圖片絕對路徑(調用上面startUCrop,即返回圖片路徑),然后調接口上傳服務器。

1 @Override2 public void onActivityResult(int requestCode, int resultCode, Intent data) {3     if (resultCode == RESULT_OK && requestCode == UCrop.REQUEST_CROP) {4         final Uri resultUri = UCrop.getOutput(data);5     } else if (resultCode == UCrop.RESULT_ERROR) {6         final Throwable cropError = UCrop.getError(data);7     }8 }

uCrop源碼淺析

uCrop源碼能學習的東西有很多,比如左右滑的標尺,不過我們這里源碼淺析只關注裁剪部分。

類關系

首先有個大概了解:

GestureCropImageView:負責監聽各種手勢
CropImageView:主要完成圖片裁剪工作,和判斷裁剪圖片是否充滿裁剪框
TransformImageView:負責圖片旋轉、縮放、位移操作

入口

由上面的效果圖可知,點擊右上角,調用裁剪操作,代碼如下:

 1 @Override 2 public boolean onOptionsItemSelected(MenuItem item) { 3     if (item.getItemId() == R.id.menu_crop) { 4         cropAndSaveImage(); 5     } 6     return super.onOptionsItemSelected(item); 7 } 8 //裁剪和保存圖片 9 protected void cropAndSaveImage() {10     ……11     mGestureCropImageView.cropAndSaveImage(mCompressFormat, mCompressQuality, new BitmapCropCallback() {12         @Override13         public void onBitmapCropped(@NonNull Uri resultUri) {14             setResultUri(resultUri, mGestureCropImageView.getTargetAspectRatio());15             finish();16         }17         @Override18         public void onCropFailure(@NonNull Throwable t) {19             setResultError(t);20             finish();21         }22     });23 }

這里調用了GestureCropImageView&cropAndSaveImage方法,如下:

 1 /** 2  * @param compressFormat  圖片壓縮格式 3  * @param compressQuality 圖片壓縮質量 4  * @param cropCallback    圖片壓縮回調 5  */ 6 public void cropAndSaveImage(@NonNull Bitmap.CompressFormat compressFormat, int                                             compressQuality,@Nullable BitmapCropCallback cropCallback) { 7     //取消所有動畫 8     cancelAllAnimations(); 9     //判斷裁剪圖片是否充滿裁剪框10     setImageToWrapCropBounds(false);11     //進行裁剪12     new BitmapCropTask(getViewBitmap(), mCropRect, RectUtils.trapToRect(mCurrentImageCorners),13             getCurrentScale(), getCurrentAngle(),14             mMaxResultImageSizeX, mMaxResultImageSizeY,15             compressFormat, compressQuality,16             getImageInputPath(), getImageOutputPath(),17             cropCallback).execute();18 }

裁剪之前

setImageToWrapCropBounds

裁剪之前,先判斷裁剪圖片是否充滿裁剪框,如果沒有,進行移動和縮放讓其充滿。

 1 public void setImageToWrapCropBounds(boolean animate) { 2     //mBitmapLaidOut圖片加載OK,isImageWrapCropBounds()檢查圖片是否充滿裁剪框 3     if (mBitmapLaidOut && !isImageWrapCropBounds()) { 4         //當前圖片中心X點 5         float currentX = mCurrentImageCenter[0]; 6         //當前圖片中心Y點 7         float currentY = mCurrentImageCenter[1]; 8         //當前圖片縮放值 9         float currentScale = getCurrentScale();10         //差量11         float deltaX = mCropRect.centerX() - currentX;12         float deltaY = mCropRect.centerY() - currentY;13         float deltaScale = 0;14         //臨時矩陣重置15         mTempMatrix.reset();16         //臨時矩陣移動17         mTempMatrix.setTranslate(deltaX, deltaY);18         //復制到新的數組19         final float[] tempCurrentImageCorners = Arrays.copyOf(mCurrentImageCorners, mCurrentImageCorners.length);20         //將此矩陣應用于二維點的數組,并編寫轉換后的指向數組的點21         mTempMatrix.mapPoints(tempCurrentImageCorners);22         //再檢查圖片是否充滿裁剪框23         boolean willImageWrapCropBoundsAfterTranslate = isImageWrapCropBounds(tempCurrentImageCorners);24         if (willImageWrapCropBoundsAfterTranslate) {25             //圖片縮進的數組26             final float[] imageIndents = calculateImageIndents();27             deltaX = -(imageIndents[0] + imageIndents[2]);28             deltaY = -(imageIndents[1] + imageIndents[3]);29         } else {30             RectF tempCropRect = new RectF(mCropRect);31             mTempMatrix.reset();32             mTempMatrix.setRotate(getCurrentAngle());33             mTempMatrix.mapRect(tempCropRect);34             //獲取裁剪圖片的邊35             final float[] currentImageSides = RectUtils.getRectSidesFromCorners(mCurrentImageCorners);36             deltaScale = Math.max(tempCropRect.width() / currentImageSides[0],37                     tempCropRect.height() / currentImageSides[1]);38             deltaScale = deltaScale * currentScale - currentScale;39         }40         if (animate) {41             //移動或縮放圖片(有動畫效果)42             post(mWrapCropBoundsRunnable = new WrapCropBoundsRunnable(43                     CropImageView.this, mImageToWrapCropBoundsAnimDuration, currentX, currentY, deltaX, deltaY,44                     currentScale, deltaScale, willImageWrapCropBoundsAfterTranslate));45         } else {46             //移動圖片47             postTranslate(deltaX, deltaY);48             if (!willImageWrapCropBoundsAfterTranslate) {49                 //縮放圖片50                 zoomInImage(currentScale + deltaScale, mCropRect.centerX(), mCropRect.centerY());51             }52         }53     }54 }

進行裁剪

裁剪放到了異步,即BitmapCropTask繼承AsyncTask,先設置原始圖片resizeScale值,然后通過ExifInterface保存新的圖片,即裁剪后的圖片

  1 public class BitmapCropTask extends AsyncTask<Void, Void, Throwable> {  2       3     ……  4     /**  5      * @param viewBitmap          裁剪圖片bitmap  6      * @param cropRect            裁剪矩形  7      * @param currentImageRect    當前圖片矩形  8      * @param currentScale        當前圖片縮放值  9      * @param currentAngle        當前圖片角度 10      * @param maxResultImageSizeX 圖片裁剪后最大寬值 11      * @param maxResultImageSizeY 圖片裁剪后最大高值 12      * @param compressFormat      圖片裁剪的格式 13      * @param compressQuality     圖片裁剪的質量 14      * @param imageInputPath      裁剪圖片路徑 15      * @param imageOutputPath     圖片裁剪后路徑 16      * @param cropCallback        裁剪回調 17      */ 18     public BitmapCropTask(@Nullable Bitmap viewBitmap, 19                           @NonNull RectF cropRect, @NonNull RectF currentImageRect, 20                           float currentScale, float currentAngle, 21                           int maxResultImageSizeX, int maxResultImageSizeY, 22                           @NonNull Bitmap.CompressFormat compressFormat, int compressQuality, 23                           @NonNull String imageInputPath, @NonNull String imageOutputPath, 24                           @Nullable BitmapCropCallback cropCallback) { 25       …… 26     } 27     @Override 28     @Nullable 29     protected Throwable doInBackground(Void... params) { 30         if (mViewBitmap == null || mViewBitmap.isRecycled()) { 31             return new NullPointerException("ViewBitmap is null or already recycled"); 32         } 33         if (mCurrentImageRect.isEmpty()) { 34             return new NullPointerException("CurrentImageRect is empty"); 35         } 36         //設置resizeScale值 37         float resizeScale = resize(); 38         try { 39             //裁剪 40             crop(resizeScale); 41             //回收 42             mViewBitmap.recycle(); 43             mViewBitmap = null; 44         } catch (Throwable throwable) { 45             return throwable; 46         } 47         return null; 48     } 49     private float resize() { 50         //初始Options 51         final BitmapFactory.Options options = new BitmapFactory.Options(); 52         //查詢該位圖,而無需分配存儲器,可獲取outHeight(圖片原始高度)和 outWidth(圖片的原始寬度) 53         options.inJustDecodeBounds = true; 54         //裁剪圖片解碼 55         BitmapFactory.decodeFile(mImageInputPath, options); 56         //原始圖片和裁剪后圖片比值 57         float scaleX = options.outWidth / mViewBitmap.getWidth(); 58         float scaleY = options.outHeight / mViewBitmap.getHeight(); 59         float resizeScale = Math.min(scaleX, scaleY); 60         mCurrentScale /= resizeScale; 61         //初始化值為1 62         resizeScale = 1; 63         if (mMaxResultImageSizeX > 0 && mMaxResultImageSizeY > 0) { 64             float cropWidth = mCropRect.width() / mCurrentScale; 65             float cropHeight = mCropRect.height() / mCurrentScale; 66             if (cropWidth > mMaxResultImageSizeX || cropHeight > mMaxResultImageSizeY) { 67                 scaleX = mMaxResultImageSizeX / cropWidth; 68                 scaleY = mMaxResultImageSizeY / cropHeight; 69                 //設置resizeScale,如果是2就是高度和寬度都是原始的一半 70                 resizeScale = Math.min(scaleX, scaleY); 71                 mCurrentScale /= resizeScale; 72             } 73         } 74         return resizeScale; 75     } 76     private boolean crop(float resizeScale) throws IOException { 77         //ExifInterface這個接口提供了圖片文件的旋轉,gps,時間等信息,從原始圖片讀出Exif標簽 78         ExifInterface originalExif = new ExifInterface(mImageInputPath); 79         int top = Math.round((mCropRect.top - mCurrentImageRect.top) / mCurrentScale); 80         int left = Math.round((mCropRect.left - mCurrentImageRect.left) / mCurrentScale); 81         int width = Math.round(mCropRect.width() / mCurrentScale); 82         int height = Math.round(mCropRect.height() / mCurrentScale); 83         //復制圖片 84         boolean cropped = cropCImg(mImageInputPath, mImageOutputPath, 85                 left, top, width, height, mCurrentAngle, resizeScale, 86                 mCompressFormat.ordinal(), mCompressQuality); 87         if (cropped) { 88             //拿到裁剪后圖片 89             copyExif(originalExif, width, height); 90         } 91         return cropped; 92     } 93     @SuppressWarnings("JniMissingFunction") 94     native public boolean cropCImg(String inputPath, String outputPath, 95                                    int left, int top, int width, int height, float angle, float resizeScale, 96                                    int format, int quality) throws IOException, OutOfMemoryError; 97     /** 98      * @param originalExif 原始圖片Exif 99      * @param width        裁剪后圖片寬100      * @param height       裁剪后圖片高101      * @throws IOException 是否異常102      */103     public void copyExif(ExifInterface originalExif, int width, int height) throws IOException {104         //Exif標簽數組105         String[] attributes = new String[]{106                 ExifInterface.TAG_APERTURE,107                 ……108         };109         //指定裁剪后圖片路徑,初始化新的ExifInterface110         ExifInterface newExif = new ExifInterface(mImageOutputPath);111         String value;112         for (String attribute : attributes) {113             value = originalExif.getAttribute(attribute);114             if (!TextUtils.isEmpty(value)) {115                 //設置Exif標簽116                 newExif.setAttribute(attribute, value);117             }118         }119         newExif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, String.valueOf(width));120         newExif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, String.valueOf(height));121         newExif.setAttribute(ExifInterface.TAG_ORIENTATION, "0");122         //保存123         newExif.saveAttributes();124     }125     @Override126     protected void onPostExecute(@Nullable Throwable t) {127         if (mCropCallback != null) {128             if (t == null) {129                 //接口回調,over130                 mCropCallback.onBitmapCropped(Uri.fromFile(new File(mImageOutputPath)));131             } else {132                 mCropCallback.onCropFailure(t);133             }134         }135     }136 }

總結

uCrop功能強大,對于我來說,有很多東西值得學習,難點如Rect包含問題(其實這塊還不是很理解),新知識如ExifInterface操作圖片,BitmapFactory顯示圖片的知識點溫故等,還有自定義左右滑的標尺,都是不錯的學習源碼。拋磚引玉至此,over。

相關文章

    無相關信息

微信掃一掃

第七城市微信公眾平臺
捕鱼达人小游戏