Fresco 二三事:图片处理之旋转、缩放、裁剪切割图片
關于Fresco加載圖片的處理,例如旋轉、裁剪切割圖片,在官方文檔也都有提到,只是感覺寫的不太詳細,正好最近項目里有類似需求,所以分享一些使用小tip,后面的朋友就不用再走彎路浪費時間了。(測試圖片分辨率1200*800)
原圖:
###旋轉圖片
/*** 旋轉圖片** @param rotate ,例如:RotationOptions.ROTATE_90*/private void rotate(SimpleDraweeView img, int rotate) {RotationOptions rotationOptions = RotationOptions.forceRotation(rotate);ImageRequest build = ImageRequestBuilder.newBuilderWithSource(getUriForFresco(this, R.mipmap.test_img)).setRotationOptions(rotationOptions).build();PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder().setImageRequest(build).build();mImageView.setController(controller);} 復制代碼使用效果:
###監聽圖片下載 首先構造監聽器:
//監聽圖片下載進度,這里只重寫了onFinalImageSet,當圖片下載完成時獲得圖片寬高等信息 ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {@Overridepublic void onFinalImageSet(String id, com.facebook.imagepipeline.image.ImageInfo imageInfo, Animatable animatable) {int viewWidth = imageInfo.getWidth();int viewHeight = imageInfo.getHeight();Toast.makeText(MainActivity.this, viewWidth + "--" + viewHeight, Toast.LENGTH_SHORT).show();}}; 復制代碼/*** 獲得圖片寬高** @param controllerListener 圖片下載監聽器*/ private void getImageInfo(ControllerListener<? super ImageInfo> controllerListener) {PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder().setControllerListener(controllerListener).setUri(getUriForFresco(this, R.mipmap.test_img)).build();mImageView.setController(controller); } 復制代碼###如果我想要1:1在手機端展示呢? 我首先想到的是1:1按照圖片的尺寸設置SimpleDraweeView的寬高并設置縮放方式為fitXY,但是果不其然,view超出屏幕的部分是無效的。
###裁剪切割圖片
既然view超出屏幕無效,那就曲線救國,讓圖片超出屏幕部分不顯示在view里就好了。 裁剪圖片首先要寫一個processor類:
/*** 切割圖片processor類* 四個成員變量和createBitmap時的參數一致,即起點的X/Y坐標、要裁剪的寬高。因為項目里還涉及到縮放,所以我調整了下參數設成百分比方便換算*/ public class CutProcess extends BasePostprocessor {private float mBeginXPercent;private float mBeginYPercent;private float mCutWidthPercent;private float mCutHeightPercent;public CutProcess(float beginXPercent, float beginYPercent, float cutWidthPercent, float cutHeightPercent) {this.mBeginXPercent = beginXPercent;this.mBeginYPercent = beginYPercent;this.mCutWidthPercent = cutWidthPercent;this.mCutHeightPercent = cutHeightPercent;}@Overridepublic CloseableReference<Bitmap> process(Bitmap sourceBitmap,PlatformBitmapFactory bitmapFactory) {int viewWidth = sourceBitmap.getWidth();int viewHeight = sourceBitmap.getHeight();int beginx = (int) (mBeginXPercent * viewWidth);int beginy = (int) (mBeginYPercent * viewHeight);int width = (int) (mCutWidthPercent * viewWidth);int height = (int) (mCutHeightPercent * viewHeight);CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap(sourceBitmap, beginx, beginy, width, height);return CloseableReference.cloneOrNull(bitmapRef);} } 復制代碼然后在ImageRequest里setProcessor:
/*** 裁剪圖片* @param processor*/private void cutPic(BasePostprocessor processor) {ImageRequest build = ImageRequestBuilder.newBuilderWithSource(getUriForFresco(this, R.mipmap.test_img)).setPostprocessor(processor).build();PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder().setImageRequest(build).build();mImageView.setController(controller);} 復制代碼調用方法:
mImageView.setLayoutParams(new RelativeLayout.LayoutParams(600, 400)); CutProcess cutProcess = new CutProcess(0, 0, 0.5f, 0.5f); cutPic(cutProcess); 復制代碼 圖片是1200800的,這里設置view的寬高為600400,可以看到圖片成功裁剪只保留原圖左上四分之一。通過設置view寬高,配合裁剪圖片,即可達到1:1顯示的效果。###旋轉+裁剪 如果是要旋轉90度后再裁剪呢?那還不簡單,直接在裁剪的基礎上,在ImageRequest里調用旋轉方法不就好了。
/*** 旋轉+裁剪圖片* @param processor*/private void rotateAndcutPic(BasePostprocessor processor, int rotate) {RotationOptions rotationOptions = RotationOptions.forceRotation(rotate);ImageRequest build = ImageRequestBuilder.newBuilderWithSource(getUriForFresco(this, R.mipmap.test_img)).setPostprocessor(processor).setRotationOptions(rotationOptions).build();PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder().setImageRequest(build).build();mImageView.setController(controller);} 復制代碼然后調用:
//例如我需要旋轉90度且寬度不變,高度方向裁剪掉一半(即保留(0,0)-(1200,400)) mImageView.setLayoutParams(new RelativeLayout.LayoutParams(400, mScreenHeight)); CutProcess cutProcess = new CutProcess(0, 0, 1f, 0.5f); rotateAndcutPic(cutProcess, RotationOptions.ROTATE_90); 復制代碼然而得到的并不是我們想要的
可以看到得到的是左半邊(0,0)-(600,800)的圖,即寬度方向被裁剪掉一般,高度方向不變,明明我在cutProcess里是設置寬度方向不變,高度方向裁剪50%,但是因為旋轉了90度,結果卻正好相反。難道是因為旋轉90度后橫縱方向也發生改變?那調換一下橫縱方向的切割比例試試看:
CutProcess cutProcess = new CutProcess(0, 0, 0.5f, 1f); rotateAndcutPic(cutProcess, RotationOptions.ROTATE_90); 復制代碼可以看到,調換橫縱切割比例后,卻得到的是下半邊(0,400)-(1200,800)。還是不正確,難道是原點也改變了?再測試一下,如果要裁剪后保留右下四分之一(600,400)-(1200,800)區域,正常無旋轉的情況下是這樣的:
mImageView.setLayoutParams(new RelativeLayout.LayoutParams(600, 400)); CutProcess cutProcess = new CutProcess(0.5f, 0.5f, 0.5f, 0.5f); rotateAndcutPic(cutProcess, RotationOptions.NO_ROTATION); 復制代碼但如果旋轉270度后,同樣代碼得到的結果卻是這樣的:
看到這里我們就清楚了,旋轉圖片后,其實(0,0)點,也就是所謂的原點也隨之變換。默認情況下,原點是(0,0),順時針旋轉90度后,原點就變成了(0,800),以此類推旋轉180度原點為(1200,800),旋轉270度原點為(1200,0)(和旋轉后的圖片的左上角相對應)。雖然是在構建ImageRequest時同時傳入旋轉和裁剪參數的,但實際上可以看作是先完成了旋轉,然后在旋轉后的基礎上,以屏幕的左上角為原點,左上角往右為x正方向,左上角往下為y正方向。
小試牛刀一下,旋轉270度后,想要裁剪后只保留原圖的左上四分之一(0,0)-(600,600),那推測就應該是(0, 0.5f, 0.5f, 0.5f)。
mImageView.setLayoutParams(new RelativeLayout.LayoutParams(400, 600)); CutProcess cutProcess = new CutProcess(0, 0.5f, 0.5f, 0.5f); rotateAndcutPic(cutProcess, RotationOptions.ROTATE_270); 復制代碼Bingo!推測正確。
旋轉+裁剪就是這個原點的變換要注意下。另外看代碼里的幾個方法,裁剪、旋轉、獲得寬高等,有沒有覺得老是要重復寫PipelineDraweeController、ImageRequest的代碼好麻煩啊。其實裁剪、旋轉等方法無非也就是添加一個參數,類似這種可變參數的復雜類的構造可以使用Builder模式封裝一下。封裝代碼就不貼在這里了。demo下載地址
//Builder模式封裝后 new FrescoBuilder(mImageView, getUriForFresco(this, R.mipmap.test_img)).cutPic(0f, 0.5f, 0.5f, 0.5f) //裁剪.setRotate(RotationOptions.ROTATE_270) //旋轉.setControllerListener(controllerListener) //設置監聽.build(); 復制代碼使用Matrix實現
繼承SimpleDraweeView自定義控件,使用Matrix實現旋轉縮放:
public class MyFresco extends SimpleDraweeView {private Matrix mMatrix;private float mScaleX = 1f;private float mScaleY = 1f;private int mViewWidth = -1;private int mViewHeight = -1;private RectF mDisplayRect = new RectF();private int mDegree = -1;public MyFresco(Context context, GenericDraweeHierarchy hierarchy) {super(context, hierarchy);init();}public MyFresco(Context context) {super(context);init();}public MyFresco(Context context, AttributeSet attrs) {super(context, attrs);init();}public void setViewInfo(int width, int Height) {mViewWidth = width;mViewHeight = Height;}private void init() {mMatrix = new Matrix();mMatrix.postScale(mScaleX, mScaleY);}/*** 縮放* @param scaleX 縮放倍數*/public void setScale(float scaleX, float scaleY) {mScaleX = scaleX;mScaleY = scaleY;mMatrix.postScale(scaleX, scaleY);invalidate();}/*** 旋轉* @param degree 角度*/public void rotate(int degree) {if (mDegree == -1) {mDegree = degree;if (mDegree != 0) {mMatrix.postRotate(degree);invalidate();if (mDegree == 90) {//旋轉后圖片超出邊界,所以要再做平移mMatrix.postTranslate(getRectWidth(), 0);} else if (mDegree == 180) {mMatrix.postTranslate(getRectWidth(), getRectHieght());} else if (mDegree == 270) {mMatrix.postTranslate(0, getRectHieght());}}} else {mDegree += degree;mMatrix.postRotate(degree); //getRectWidth是旋轉后的widthinvalidate();mMatrix.postTranslate(getRectWidth(), 0);}invalidate();}/*** 還原設置*/public void reset() {mScaleX = 1f;mScaleY = 1f;mMatrix = new Matrix();mMatrix.setScale(mScaleX, mScaleY);mViewWidth = -1;mViewHeight = -1;mDegree = -1;}/*** 獲得旋轉后超出邊界的高度* @return*/public float getRectHieght() {RectF displayRect = getDisplayRect(mMatrix);if (displayRect != null) {return displayRect.height();} else {return -1;}}/*** 獲得旋轉后超出邊界的寬度* @return*/public float getRectWidth() {RectF displayRect = getDisplayRect(mMatrix);if (displayRect != null) {return displayRect.width();} else {return -1;}}private RectF getDisplayRect(Matrix matrix) {if (mViewWidth == -1 || mViewHeight == -1) {return null;}mDisplayRect.set(0.0F, 0.0F, mViewWidth, mViewHeight);getHierarchy().getActualImageBounds(mDisplayRect);//將matrix映射到rectfmatrix.mapRect(mDisplayRect);return mDisplayRect;}@Overrideprotected void onDraw(Canvas canvas) {int save = canvas.save();canvas.concat(mMatrix);super.onDraw(canvas);canvas.restoreToCount(save);} }復制代碼就是醬~
總結
以上是生活随笔為你收集整理的Fresco 二三事:图片处理之旋转、缩放、裁剪切割图片的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Source Map调试压缩后代码
- 下一篇: ArrayList去除集合中字符串的重复