Android 以任意比例裁剪图片代码分享
公司的一个小伙伴写的,可以按照任意比例裁剪图片。我觉得挺好用的。简单在这里记录一下,以后肯定还会用到。
publicclassSeniorCropImageViewextendsImageViewimplementsScaleGestureDetector.OnScaleGestureListener, View.OnLayoutChangeListener{ /*Fordrawingcolorfieldstart*/ privatestaticfinalintLINE_COLOR=Color.WHITE; privatestaticfinalintOUTER_MASK_COLOR=Color.argb(191,0,0,0); privatestaticfinalintLINE_WIDTH_IN_DP=1; privatefinalfloat[]mMatrixValues=newfloat[9]; protectedMatrixmSupportMatrix; protectedScaleGestureDetectormScaleGestureDetector; /*Fordrawingcolorfieldend*/ protectedPaintmPaint; /* *宽比高 */ protectedfloatmRatio=1.0f; protectedRectFmCropRect; //RectFPadding是适应产品需求,给裁剪框mCropRect设置一下padding--chenglin2016年04月18日 protectedfloatRectFPadding=0; protectedintmLastX; protectedintmLastY; protectedOPERATIONmOperation; privateonBitmapLoadListeneriBitmapLoading=null; privatebooleanmEnableDrawCropWidget=true; /* Forscaleanddrag */ privateMatrixmBaseMatrix; privateMatrixmDrawMatrix; privateAccelerateDecelerateInterpolatorsInterpolator=newAccelerateDecelerateInterpolator(); privatePathmPath; privateintmLineWidth; privatefloatmScaleMax=3.0f; privateRectFmBoundaryRect; privateintmRotation=0; privateintmImageWidth; privateintmImageHeight; privateintmDisplayW; privateintmDisplayH; publicSeniorCropImageView(Contextcontext){ this(context,null); } publicSeniorCropImageView(Contextcontext,AttributeSetattrs){ this(context,attrs,0); } publicSeniorCropImageView(Contextcontext,AttributeSetattrs,intdefStyleAttr){ super(context,attrs,defStyleAttr); if(attrs!=null){ TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.Life_CropImage); mRatio=a.getFloat(R.styleable.Life_CropImage_life_Crop_ratio,1.0f); a.recycle(); } init(); } publicstaticvoiddecodeImageForCropping(finalStringpath,finalIDecodeCallbackcallback){ newThread(newRunnable(){ @Override publicvoidrun(){ introtation=0; //读取一下exif中的rotation try{ ExifInterfaceexif=newExifInterface(path); finalintrotate=exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_UNDEFINED); switch(rotate){ caseExifInterface.ORIENTATION_ROTATE_90: rotation=90; break; caseExifInterface.ORIENTATION_ROTATE_180: rotation=180; break; caseExifInterface.ORIENTATION_ROTATE_270: rotation=270; break; } }catch(IOExceptione){ e.printStackTrace(); } finalBitmapFactory.Optionsoptions=newBitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeFile(path,options); finalinttextureLimit=getMaxTextureSize(); intscale=1; while(options.outWidth/scale>=textureLimit){ scale*=2; } while(options.outHeight/scale>=textureLimit){ scale*=2; } options.inSampleSize=scale; options.inJustDecodeBounds=false; Bitmapbitmap=null; try{ bitmap=BitmapFactory.decodeFile(path,options); }catch(OutOfMemoryErrore){ e.printStackTrace(); } finalBitmapbimapDecoded=bitmap; if(bimapDecoded==null){ return; } if(callback!=null){ callback.onDecoded(rotation,bimapDecoded); } } }).start(); } privatestaticintgetMaxTextureSize(){ EGL10egl=(EGL10)EGLContext.getEGL(); EGLDisplaydisplay=egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); //Initialise int[]version=newint[2]; egl.eglInitialize(display,version); //Querytotalnumberofconfigurations int[]totalConfigurations=newint[1]; egl.eglGetConfigs(display,null,0,totalConfigurations); //Queryactuallistconfigurations EGLConfig[]configurationsList=newEGLConfig[totalConfigurations[0]]; egl.eglGetConfigs(display,configurationsList,totalConfigurations[0],totalConfigurations); int[]textureSize=newint[1]; intmaximumTextureSize=0; //Iteratethroughalltheconfigurationstolocatedthemaximumtexturesize for(inti=0;i<totalConfigurations[0];i++){ //Onlyneedtocheckforwidthsinceopengltexturesarealwayssquared egl.eglGetConfigAttrib(display,configurationsList[i],EGL10.EGL_MAX_PBUFFER_WIDTH,textureSize); //Keeptrackofthemaximumtexturesize if(maximumTextureSize<textureSize[0]){ maximumTextureSize=textureSize[0]; } } //Release egl.eglTerminate(display); returnmaximumTextureSize; } @Override publicvoidonLayoutChange(Viewv,intleft,inttop,intright,intbottom,intoldLeft,intoldTop,intoldRight,intoldBottom){ mDisplayW=right-left; mDisplayH=bottom-top; if(getDrawable()!=null&&((BitmapDrawable)getDrawable()).getBitmap()!=null){ calculateProperties(((BitmapDrawable)getDrawable()).getBitmap()); } } privatevoidinit(){ mScaleGestureDetector=newScaleGestureDetector(getContext(),this); mBaseMatrix=newMatrix(); mDrawMatrix=newMatrix(); mSupportMatrix=newMatrix(); mLineWidth=(int)dipToPixels(LINE_WIDTH_IN_DP); mPaint=newPaint(); //表示第一个实线段长dashOnWidth,第一个虚线段长dashOffWidth mPath=newPath(); mCropRect=newRectF(); mBoundaryRect=newRectF(); setScaleType(ScaleType.MATRIX); setClickable(true); } privatefloatdipToPixels(floatdip){ returnTypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip, getResources().getDisplayMetrics()); } @Override protectedvoidonAttachedToWindow(){ super.onAttachedToWindow(); addOnLayoutChangeListener(this); } @Override protectedvoidonDetachedFromWindow(){ super.onDetachedFromWindow(); removeOnLayoutChangeListener(this); } /** *设置图片的裁剪比例,比如3:4就是0.75 * *@paramratio */ publicvoidsetCropRatio(finalfloatratio){ if(mRatio==ratio){ return; } mRatio=ratio; //重新选择比例后,恢复旋转角度 //setImageRotation(0); if(getDrawable()==null){ return; } calculateProperties(((BitmapDrawable)getDrawable()).getBitmap()); postInvalidate(); } publicvoidsetImageRotation(introtation){ if(mRotation==rotation){ return; } mRotation=rotation; if(getDrawable()==null){ return; } calculateProperties(((BitmapDrawable)getDrawable()).getBitmap()); postInvalidate(); } publicvoidsetCropRectPadding(floatpadding){ RectFPadding=padding; } publicvoidsetImagePath(finalStringpath){ if(TextUtils.isEmpty(path)){ return; } if(iBitmapLoading!=null){ iBitmapLoading.onLoadPrepare(); } decodeImageForCropping(path,newIDecodeCallback(){ @Override publicvoidonDecoded(finalintrotation,finalBitmapbitmap){ post(newRunnable(){ @Override publicvoidrun(){ mRotation=rotation; setImageBitmap(bitmap); if(iBitmapLoading!=null){ iBitmapLoading.onLoadFinish(); } } }); } }); } @Override publicvoidsetImageBitmap(Bitmapbm){ calculateProperties(bm); super.setImageBitmap(bm); } publicvoidsetBitmapLoadingListener(onBitmapLoadListeneriBitmapLoad){ iBitmapLoading=iBitmapLoad; } protectedvoidcalculateProperties(Bitmapbm){ mSupportMatrix.reset(); mBaseMatrix.reset(); intwidthSize=mDisplayW; intheightSize=mDisplayH; generateCropRect(widthSize,heightSize); mImageWidth=bm.getWidth(); mImageHeight=bm.getHeight(); finalbooleanrotated=isImageRotated(); finalintbitmapWidth=rotated?mImageHeight:mImageWidth; finalintbitmapHeight=rotated?mImageWidth:mImageHeight; mBoundaryRect.set(0,0,bitmapWidth,bitmapHeight); finalfloatwidthScale=mCropRect.width()/bitmapWidth; finalfloatheightScale=mCropRect.height()/bitmapHeight; finalfloatscale=Math.max(widthScale,heightScale); finalfloatscaledHeight=scale*bitmapHeight; finalfloatscaledWidth=scale*bitmapWidth; //移动到中心点 finalinttranslateX=(int)(mCropRect.left+mCropRect.width()/2-scaledWidth/2); finalinttranslateY=(int)(mCropRect.top+mCropRect.height()/2-scaledHeight/2); mBaseMatrix.setScale(scale,scale); mBaseMatrix.postTranslate(translateX,translateY); mBaseMatrix.mapRect(mBoundaryRect); setImageMatrix(getDrawMatrix()); } privatebooleanisImageRotated(){ return((mRotation%360)==90)||((mRotation%360)==270); } privatevoidgenerateCropRect(intboundaryWidth,intboundaryHeight){ //RectFPadding是适应产品需求,给裁剪框mCropRect设置一下padding--chenglin2016年04月18日 boundaryWidth=boundaryWidth-(int)(RectFPadding*2); boundaryHeight=boundaryHeight-(int)(RectFPadding*2); intleft; inttop; intright; intbottom; booleanvertical; //宽/高大于比例的话,说明裁剪框是“竖直”的 vertical=(float)boundaryWidth/boundaryHeight>mRatio; finalintrectH=(int)(boundaryWidth/mRatio); finalintrectW=(int)(boundaryHeight*mRatio); if(vertical){ left=(boundaryWidth-rectW)/2; top=0; right=(boundaryWidth+rectW)/2; bottom=boundaryHeight; }else{ left=0; top=(boundaryHeight-rectH)/2; right=boundaryWidth; bottom=(boundaryHeight+rectH)/2; } //RectFPadding是适应产品需求,给裁剪框mCropRect设置一下padding--chenglin2016年04月18日 mCropRect.set(left+RectFPadding,top+RectFPadding,right+RectFPadding,bottom+RectFPadding); } @Override protectedvoidonDraw(Canvascanvas){ super.onDraw(canvas); if(!mEnableDrawCropWidget){ return; } if(getDrawable()==null){ return; } mPaint.reset(); mPaint.setAntiAlias(true); mPaint.setColor(LINE_COLOR); mPaint.setStrokeWidth(mLineWidth); mPaint.setStyle(Paint.Style.STROKE); mPath.reset(); //上 mPath.moveTo(mCropRect.left,mCropRect.top); mPath.lineTo(mCropRect.right,mCropRect.top); //左 mPath.moveTo(mCropRect.left,mCropRect.top); mPath.lineTo(mCropRect.left,mCropRect.bottom); //右 mPath.moveTo(mCropRect.right,mCropRect.top); mPath.lineTo(mCropRect.right,mCropRect.bottom); //下 mPath.moveTo(mCropRect.right,mCropRect.bottom); mPath.lineTo(mCropRect.left,mCropRect.bottom); canvas.drawPath(mPath,mPaint); //绘制外部阴影部分 mPaint.reset(); mPaint.setAntiAlias(true); mPaint.setColor(Color.parseColor("#B3333333")); mPaint.setStyle(Paint.Style.FILL); //下面的四个矩形是装饰性的,就是裁剪框四周的四个阴影 finalintlineOffset=mLineWidth; if(mCropRect.top>0){ canvas.drawRect(0,0,getMeasuredWidth(),mCropRect.top-lineOffset,mPaint); } if(mCropRect.left>0){ canvas.drawRect(mCropRect.top-lineOffset-RectFPadding,RectFPadding-lineOffset,mCropRect.left-lineOffset,mCropRect.bottom+lineOffset,mPaint); } if(mCropRect.right<getMeasuredWidth()){ canvas.drawRect(mCropRect.right+lineOffset,mCropRect.top-lineOffset,getMeasuredWidth(),mCropRect.bottom+lineOffset,mPaint); } if(mCropRect.bottom<getMeasuredHeight()){ canvas.drawRect(0,mCropRect.bottom+lineOffset,getMeasuredWidth(),getMeasuredHeight(),mPaint); } } publicbooleanonTouchEvent(MotionEventev){ if(ev.getPointerCount()>1){ mOperation=OPERATION.SCALE; returnmScaleGestureDetector.onTouchEvent(ev); } finalintaction=ev.getActionMasked(); finalintx=(int)ev.getX(); finalinty=(int)ev.getY(); switch(action){ caseMotionEvent.ACTION_DOWN: mOperation=OPERATION.DRAG; mLastX=x; mLastY=y; break; caseMotionEvent.ACTION_MOVE: if(mOperation==OPERATION.DRAG){ intdeltaX=x-mLastX; intdeltaY=y-mLastY; RectFboundary=getDrawBoundary(getDrawMatrix()); if(boundary.left+deltaX>mCropRect.left){ deltaX=(int)(mCropRect.left-boundary.left); }elseif(boundary.right+deltaX<mCropRect.right){ deltaX=(int)(mCropRect.right-boundary.right); } if(boundary.top+deltaY>mCropRect.top){ deltaY=(int)(mCropRect.top-boundary.top); }elseif(boundary.bottom+deltaY<mCropRect.bottom){ deltaY=(int)(mCropRect.bottom-boundary.bottom); } mSupportMatrix.postTranslate(deltaX,deltaY); setImageMatrix(getDrawMatrix()); mLastX=x; mLastY=y; } break; caseMotionEvent.ACTION_CANCEL: caseMotionEvent.ACTION_POINTER_UP: caseMotionEvent.ACTION_UP: mLastX=0; mLastY=0; mOperation=null; break; } returnsuper.onTouchEvent(ev); } publicBitmapgetOriginBitmap(){ BitmapDrawabledrawable=(BitmapDrawable)getDrawable(); returndrawable==null?null:drawable.getBitmap(); } /** *保存图片为bitmap */ publicBitmapsaveCrop()throwsOutOfMemoryError{ if(getDrawable()==null){ returnnull; } Bitmaporigin=getOriginBitmap(); MatrixdrawMatrix=getDrawMatrix(); //反转一下矩阵 Matrixinverse=newMatrix(); drawMatrix.invert(inverse); //把裁剪框对应到原图上去 RectFcropMapped=newRectF(); inverse.mapRect(cropMapped,mCropRect); clampCropRect(cropMapped,origin.getWidth(),origin.getHeight()); //如果产生了旋转,需要一个旋转矩阵 MatrixrotationM=newMatrix(); if(mRotation%360!=0){ rotationM.postRotate(mRotation,origin.getWidth()/2,origin.getHeight()/2); } Bitmapcropped=Bitmap.createBitmap( origin,(int)cropMapped.left,(int)cropMapped.top,(int)cropMapped.width(),(int)cropMapped.height(),rotationM,true ); returncropped; } privatevoidclampCropRect(RectFcropRect,intborderW,intborderH){ if(cropRect.left<0){ cropRect.left=0; } if(cropRect.top<0){ cropRect.top=0; } if(cropRect.right>borderW){ cropRect.right=borderW; } if(cropRect.bottom>borderH){ cropRect.bottom=borderH; } } @Override publicbooleanonScale(ScaleGestureDetectordetector){ floatscale=detector.getScaleFactor(); if(scale==1.0f){ returntrue; } finalfloatcurrentScale=getScale(mSupportMatrix); finalfloatcenterX=detector.getFocusX(); finalfloatcenterY=detector.getFocusY(); if((currentScale<=1.0f&&scale<1.0f) ||(currentScale>=mScaleMax&&scale>1.0f)){ returntrue; } if(currentScale*scale<1.0f){ scale=1.0f/currentScale; }elseif(currentScale*scale>mScaleMax){ scale=mScaleMax/currentScale; } mSupportMatrix.postScale(scale,scale,centerX,centerY); RectFboundary=getDrawBoundary(getDrawMatrix()); floattranslateX=0; if(boundary.left>mCropRect.left){ translateX=mCropRect.left-boundary.left; }elseif(boundary.right<mCropRect.right){ translateX=mCropRect.right-boundary.right; } Log.d("scale","x==>"+translateX); floattranslateY=0; if(boundary.top>mCropRect.top){ translateY=mCropRect.top-boundary.top; }elseif(boundary.bottom<mCropRect.bottom){ translateY=mCropRect.bottom-boundary.bottom; } mSupportMatrix.postTranslate(translateX,translateY); setImageMatrix(getDrawMatrix()); returntrue; } protectedMatrixgetDrawMatrix(){ mDrawMatrix.reset(); if(mRotation%360!=0){ finalbooleanrotated=isImageRotated(); finalintwidth=rotated?mImageHeight:mImageWidth; finalintheight=rotated?mImageWidth:mImageHeight; mDrawMatrix.postRotate(mRotation,mImageWidth/2,mImageHeight/2); if(rotated){ finalinttranslateX=(width-mImageWidth)/2; finalinttranslateY=(height-mImageHeight)/2; mDrawMatrix.postTranslate(translateX,translateY); } } mDrawMatrix.postConcat(mBaseMatrix); mDrawMatrix.postConcat(mSupportMatrix); returnmDrawMatrix; } @Override publicbooleanonScaleBegin(ScaleGestureDetectordetector){ returntrue; } @Override publicvoidonScaleEnd(ScaleGestureDetectordetector){ finalfloatcurrentScale=getScale(mSupportMatrix); if(currentScale<1.0f){ Log.e("onScaleEnd","currentScale==>"+currentScale); RectFboundary=getDrawBoundary(getDrawMatrix()); post(newAnimatedZoomRunnable(currentScale,1.0f,boundary.centerX(),boundary.centerY())); } } protectedRectFgetDrawBoundary(Matrixmatrix){ Drawabledrawable=getDrawable(); if(drawable==null){ returnmBoundaryRect; } finalintbitmapWidth=drawable.getIntrinsicWidth(); finalintbitmapHeight=drawable.getIntrinsicHeight(); mBoundaryRect.set(0,0,bitmapWidth,bitmapHeight); matrix.mapRect(mBoundaryRect); returnmBoundaryRect; } publicfloatgetScale(Matrixmatrix){ return(float)Math.sqrt((float)Math.pow(getValue(matrix,Matrix.MSCALE_X),2)+(float)Math.pow(getValue(matrix,Matrix.MSKEW_Y),2)); } /** *Helpermethodthat'unpacks'aMatrixandreturnstherequiredvalue * *@parammatrix-Matrixtounpack *@paramwhichValue-WhichvaluefromMatrix.M*toreturn *@returnfloat-returnedvalue */ privatefloatgetValue(Matrixmatrix,intwhichValue){ matrix.getValues(mMatrixValues); returnmMatrixValues[whichValue]; } publicvoidenableDrawCropWidget(booleanenable){ mEnableDrawCropWidget=enable; } protectedenumOPERATION{ DRAG,SCALE } publicenumType{ CENTER_CROP,CENTER_INSIDE } publicinterfaceIDecodeCallback{ voidonDecoded(finalintrotation,finalBitmapbitmap); } //setImagePath这个方法耗时,需要显示进度条,这个是监听 publicinterfaceonBitmapLoadListener{ voidonLoadPrepare(); voidonLoadFinish(); } privateclassAnimatedZoomRunnableimplementsRunnable{ privatefinalfloatmFocalX,mFocalY; privatefinallongmStartTime; privatefinalfloatmZoomStart,mZoomEnd; publicAnimatedZoomRunnable(finalfloatcurrentZoom,finalfloattargetZoom, finalfloatfocalX,finalfloatfocalY){ mFocalX=focalX; mFocalY=focalY; mStartTime=System.currentTimeMillis(); mZoomStart=currentZoom; mZoomEnd=targetZoom; } @Override publicvoidrun(){ floatt=interpolate(); floatscale=mZoomStart+t*(mZoomEnd-mZoomStart); floatdeltaScale=scale/getScale(mSupportMatrix); mSupportMatrix.postScale(deltaScale,deltaScale,mFocalX,mFocalY); setImageMatrix(getDrawMatrix()); //Wehaven'thitourtargetscaleyet,sopostourselvesagain if(t<1f){ postOnAnimation(this); } } privatefloatinterpolate(){ floatt=1f*(System.currentTimeMillis()-mStartTime)/200; t=Math.min(1f,t); t=sInterpolator.getInterpolation(t); returnt; } } } <declare-styleablename="Life_CropImage"> <attrname="life_Crop_ratio"format="float"/> <attrname="life_Crop_scale_type"format="enum"> <enumname="life_center_crop"value="0"/> <enumname="life_center_inside"value="1"/> </attr> </declare-styleable>
1、让这个裁剪框显示图片:
mSeniorImageView.setImagePath(path);
2、保存裁剪后的图片:
BitmapimageViewBitmap=null; try{ imageViewBitmap=mSeniorImageView.saveCrop(); }catch(OutOfMemoryErrore){ imageViewBitmap=mSeniorImageView.getOriginBitmap(); PinkToast.makeText(mActivity,R.string.life_image_crop_topbar_crop_error,Toast.LENGTH_LONG).show(); }
3、设置裁剪比例:
mSeniorImageView.setCropRatio(3f/4f);
4、设置裁剪框的padding:
mSeniorImageView.setCropRectPadding(0f);
5、setImagePath这个方法比较耗时,需要显示进度条,这个是监听:
mSeniorImageView.setBitmapLoadingListener(newSeniorCropImageView.onBitmapLoadListener(){ @Override publicvoidonLoadPrepare(){ mActivity.showProgress(); } @Override publicvoidonLoadFinish(){ mActivity.hideProgress(); } });
以上所述是小编给大家带来的Android以任意比例裁剪图片代码分享,希望对大家有所帮助