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以任意比例裁剪图片代码分享,希望对大家有所帮助