欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 以任意比例裁剪图片代码分享

程序员文章站 2024-03-02 14:46:10
公司的一个小伙伴写的,可以按照任意比例裁剪图片。我觉得挺好用的。简单在这里记录一下,以后肯定还会用到。 public class seniorcropimagev...

公司的一个小伙伴写的,可以按照任意比例裁剪图片。我觉得挺好用的。简单在这里记录一下,以后肯定还会用到。

public class seniorcropimageview extends imageview implements scalegesturedetector.onscalegesturelistener, 
view.onlayoutchangelistener { 
/* for drawing color field start */ 
private static final int line_color = color.white; 
private static final int outer_mask_color = color.argb(191, 0, 0, 0); 
private static final int line_width_in_dp = 1; 
private final float[] mmatrixvalues = new float[9]; 
protected matrix msupportmatrix; 
protected scalegesturedetector mscalegesturedetector; 
/* for drawing color field end */ 
protected paint mpaint; 
/* 
* 宽比高 
*/ 
protected float mratio = 1.0f; 
protected rectf mcroprect; 
//rectfpadding是适应产品需求,给裁剪框mcroprect设置一下padding -- chenglin 2016年04月18日 
protected float rectfpadding = 0; 
protected int mlastx; 
protected int mlasty; 
protected operation moperation; 
private onbitmaploadlistener ibitmaploading = null; 
private boolean menabledrawcropwidget = true; 
/* 
for scale and drag 
*/ 
private matrix mbasematrix; 
private matrix mdrawmatrix; 
private acceleratedecelerateinterpolator sinterpolator = new acceleratedecelerateinterpolator(); 
private path mpath; 
private int mlinewidth; 
private float mscalemax = 3.0f; 
private rectf mboundaryrect; 
private int mrotation = 0; 
private int mimagewidth; 
private int mimageheight; 
private int mdisplayw; 
private int mdisplayh; 
public seniorcropimageview(context context) { 
this(context, null); 
} 
public seniorcropimageview(context context, attributeset attrs) { 
this(context, attrs, 0); 
} 
public seniorcropimageview(context context, attributeset attrs, int defstyleattr) { 
super(context, attrs, defstyleattr); 
if (attrs != null) { 
typedarray a = context.obtainstyledattributes(attrs, r.styleable.life_cropimage); 
mratio = a.getfloat(r.styleable.life_cropimage_life_crop_ratio, 1.0f); 
a.recycle(); 
} 
init(); 
} 
public static void decodeimageforcropping(final string path, final idecodecallback callback) { 
new thread(new runnable() { 
@override 
public void run() { 
int rotation = 0; 
// 读取一下exif中的rotation 
try { 
exifinterface exif = new exifinterface(path); 
final int rotate = exif.getattributeint(exifinterface.tag_orientation, exifinterface.orientation_undefined); 
switch (rotate) { 
case exifinterface.orientation_rotate_90: 
rotation = 90; 
break; 
case exifinterface.orientation_rotate_180: 
rotation = 180; 
break; 
case exifinterface.orientation_rotate_270: 
rotation = 270; 
break; 
} 
} catch (ioexception e) { 
e.printstacktrace(); 
} 
final bitmapfactory.options options = new bitmapfactory.options(); 
options.injustdecodebounds = true; 
bitmapfactory.decodefile(path, options); 
final int texturelimit = getmaxtexturesize(); 
int scale = 1; 
while (options.outwidth / scale >= texturelimit) { 
scale *= 2; 
} 
while (options.outheight / scale >= texturelimit) { 
scale *= 2; 
} 
options.insamplesize = scale; 
options.injustdecodebounds = false; 
bitmap bitmap = null; 
try { 
bitmap = bitmapfactory.decodefile(path, options); 
} catch (outofmemoryerror e) { 
e.printstacktrace(); 
} 
final bitmap bimapdecoded = bitmap; 
if (bimapdecoded == null) { 
return; 
} 
if (callback != null) { 
callback.ondecoded(rotation, bimapdecoded); 
} 
} 
}).start(); 
} 
private static int getmaxtexturesize() { 
egl10 egl = (egl10) eglcontext.getegl(); 
egldisplay display = egl.eglgetdisplay(egl10.egl_default_display); 
// initialise 
int[] version = new int[2]; 
egl.eglinitialize(display, version); 
// query total number of configurations 
int[] totalconfigurations = new int[1]; 
egl.eglgetconfigs(display, null, 0, totalconfigurations); 
// query actual list configurations 
eglconfig[] configurationslist = new eglconfig[totalconfigurations[0]]; 
egl.eglgetconfigs(display, configurationslist, totalconfigurations[0], totalconfigurations); 
int[] texturesize = new int[1]; 
int maximumtexturesize = 0; 
// iterate through all the configurations to located the maximum texture size 
for (int i = 0; i < totalconfigurations[0]; i++) { 
// only need to check for width since opengl textures are always squared 
egl.eglgetconfigattrib(display, configurationslist[i], egl10.egl_max_pbuffer_width, texturesize); 
// keep track of the maximum texture size 
if (maximumtexturesize < texturesize[0]) { 
maximumtexturesize = texturesize[0]; 
} 
} 
// release 
egl.eglterminate(display); 
return maximumtexturesize; 
} 
@override 
public void onlayoutchange(view v, int left, int top, int right, int bottom, int oldleft, int oldtop, int oldright, int oldbottom) { 
mdisplayw = right - left; 
mdisplayh = bottom - top; 
if (getdrawable() != null && ((bitmapdrawable) getdrawable()).getbitmap() != null) { 
calculateproperties(((bitmapdrawable) getdrawable()).getbitmap()); 
} 
} 
private void init() { 
mscalegesturedetector = new scalegesturedetector(getcontext(), this); 
mbasematrix = new matrix(); 
mdrawmatrix = new matrix(); 
msupportmatrix = new matrix(); 
mlinewidth = (int) diptopixels(line_width_in_dp); 
mpaint = new paint(); 
// 表示第一个实线段长dashonwidth,第一个虚线段长dashoffwidth 
mpath = new path(); 
mcroprect = new rectf(); 
mboundaryrect = new rectf(); 
setscaletype(scaletype.matrix); 
setclickable(true); 
} 
private float diptopixels(float dip) { 
return typedvalue.applydimension(typedvalue.complex_unit_dip, dip, 
getresources().getdisplaymetrics()); 
} 
@override 
protected void onattachedtowindow() { 
super.onattachedtowindow(); 
addonlayoutchangelistener(this); 
} 
@override 
protected void ondetachedfromwindow() { 
super.ondetachedfromwindow(); 
removeonlayoutchangelistener(this); 
} 
/** 
* 设置图片的裁剪比例,比如3:4就是0.75 
* 
* @param ratio 
*/ 
public void setcropratio(final float ratio) { 
if (mratio == ratio) { 
return; 
} 
mratio = ratio; 
//重新选择比例后,恢复旋转角度 
//setimagerotation(0); 
if (getdrawable() == null) { 
return; 
} 
calculateproperties(((bitmapdrawable) getdrawable()).getbitmap()); 
postinvalidate(); 
} 
public void setimagerotation(int rotation) { 
if (mrotation == rotation) { 
return; 
} 
mrotation = rotation; 
if (getdrawable() == null) { 
return; 
} 
calculateproperties(((bitmapdrawable) getdrawable()).getbitmap()); 
postinvalidate(); 
} 
public void setcroprectpadding(float padding) { 
rectfpadding = padding; 
} 
public void setimagepath(final string path) { 
if (textutils.isempty(path)) { 
return; 
} 
if (ibitmaploading != null) { 
ibitmaploading.onloadprepare(); 
} 
decodeimageforcropping(path, new idecodecallback() { 
@override 
public void ondecoded(final int rotation, final bitmap bitmap) { 
post(new runnable() { 
@override 
public void run() { 
mrotation = rotation; 
setimagebitmap(bitmap); 
if (ibitmaploading != null) { 
ibitmaploading.onloadfinish(); 
} 
} 
}); 
} 
}); 
} 
@override 
public void setimagebitmap(bitmap bm) { 
calculateproperties(bm); 
super.setimagebitmap(bm); 
} 
public void setbitmaploadinglistener(onbitmaploadlistener ibitmapload) { 
ibitmaploading = ibitmapload; 
} 
protected void calculateproperties(bitmap bm) { 
msupportmatrix.reset(); 
mbasematrix.reset(); 
int widthsize = mdisplayw; 
int heightsize = mdisplayh; 
generatecroprect(widthsize, heightsize); 
mimagewidth = bm.getwidth(); 
mimageheight = bm.getheight(); 
final boolean rotated = isimagerotated(); 
final int bitmapwidth = rotated ? mimageheight : mimagewidth; 
final int bitmapheight = rotated ? mimagewidth : mimageheight; 
mboundaryrect.set(0, 0, bitmapwidth, bitmapheight); 
final float widthscale = mcroprect.width() / bitmapwidth; 
final float heightscale = mcroprect.height() / bitmapheight; 
final float scale = math.max(widthscale, heightscale); 
final float scaledheight = scale * bitmapheight; 
final float scaledwidth = scale * bitmapwidth; 
// 移动到中心点 
final int translatex = (int) (mcroprect.left + mcroprect.width() / 2 - scaledwidth / 2); 
final int translatey = (int) (mcroprect.top + mcroprect.height() / 2 - scaledheight / 2); 
mbasematrix.setscale(scale, scale); 
mbasematrix.posttranslate(translatex, translatey); 
mbasematrix.maprect(mboundaryrect); 
setimagematrix(getdrawmatrix()); 
} 
private boolean isimagerotated() { 
return ((mrotation % 360) == 90) || ((mrotation % 360) == 270); 
} 
private void generatecroprect(int boundarywidth, int boundaryheight) { 
//rectfpadding是适应产品需求,给裁剪框mcroprect设置一下padding -- chenglin 2016年04月18日 
boundarywidth = boundarywidth - (int)(rectfpadding * 2); 
boundaryheight = boundaryheight - (int)(rectfpadding * 2); 
int left; 
int top; 
int right; 
int bottom; 
boolean vertical; 
// 宽/高 大于比例的话,说明裁剪框是“竖直”的 
vertical = (float) boundarywidth / boundaryheight > mratio; 
final int recth = (int) (boundarywidth / mratio); 
final int rectw = (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 -- chenglin 2016年04月18日 
mcroprect.set(left + rectfpadding, top + rectfpadding, right + rectfpadding, bottom + rectfpadding); 
} 
@override 
protected void ondraw(canvas canvas) { 
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); 
//下面的四个矩形是装饰性的,就是裁剪框四周的四个阴影 
final int lineoffset = 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); 
} 
} 
public boolean ontouchevent(motionevent ev) { 
if (ev.getpointercount() > 1) { 
moperation = operation.scale; 
return mscalegesturedetector.ontouchevent(ev); 
} 
final int action = ev.getactionmasked(); 
final int x = (int) ev.getx(); 
final int y = (int) ev.gety(); 
switch (action) { 
case motionevent.action_down: 
moperation = operation.drag; 
mlastx = x; 
mlasty = y; 
break; 
case motionevent.action_move: 
if (moperation == operation.drag) { 
int deltax = x - mlastx; 
int deltay = y - mlasty; 
rectf boundary = getdrawboundary(getdrawmatrix()); 
if (boundary.left + deltax > mcroprect.left) { 
deltax = (int) (mcroprect.left - boundary.left); 
} else if (boundary.right + deltax < mcroprect.right) { 
deltax = (int) (mcroprect.right - boundary.right); 
} 
if (boundary.top + deltay > mcroprect.top) { 
deltay = (int) (mcroprect.top - boundary.top); 
} else if (boundary.bottom + deltay < mcroprect.bottom) { 
deltay = (int) (mcroprect.bottom - boundary.bottom); 
} 
msupportmatrix.posttranslate(deltax, deltay); 
setimagematrix(getdrawmatrix()); 
mlastx = x; 
mlasty = y; 
} 
break; 
case motionevent.action_cancel: 
case motionevent.action_pointer_up: 
case motionevent.action_up: 
mlastx = 0; 
mlasty = 0; 
moperation = null; 
break; 
} 
return super.ontouchevent(ev); 
} 
public bitmap getoriginbitmap() { 
bitmapdrawable drawable = (bitmapdrawable) getdrawable(); 
return drawable == null ? null : drawable.getbitmap(); 
} 
/** 
* 保存图片为bitmap 
*/ 
public bitmap savecrop() throws outofmemoryerror { 
if (getdrawable() == null) { 
return null; 
} 
bitmap origin = getoriginbitmap(); 
matrix drawmatrix = getdrawmatrix(); 
// 反转一下矩阵 
matrix inverse = new matrix(); 
drawmatrix.invert(inverse); 
// 把裁剪框对应到原图上去 
rectf cropmapped = new rectf(); 
inverse.maprect(cropmapped, mcroprect); 
clampcroprect(cropmapped, origin.getwidth(), origin.getheight()); 
// 如果产生了旋转,需要一个旋转矩阵 
matrix rotationm = new matrix(); 
if (mrotation % 360 != 0) { 
rotationm.postrotate(mrotation, origin.getwidth() / 2, origin.getheight() / 2); 
} 
bitmap cropped = bitmap.createbitmap( 
origin, (int) cropmapped.left, (int) cropmapped.top, (int) cropmapped.width(), (int) cropmapped.height(), rotationm, true 
); 
return cropped; 
} 
private void clampcroprect(rectf croprect, int borderw, int borderh) { 
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 
public boolean onscale(scalegesturedetector detector) { 
float scale = detector.getscalefactor(); 
if (scale == 1.0f) { 
return true; 
} 
final float currentscale = getscale(msupportmatrix); 
final float centerx = detector.getfocusx(); 
final float centery = detector.getfocusy(); 
if ((currentscale <= 1.0f && scale < 1.0f) 
|| (currentscale >= mscalemax && scale > 1.0f)) { 
return true; 
} 
if (currentscale * scale < 1.0f) { 
scale = 1.0f / currentscale; 
} else if (currentscale * scale > mscalemax) { 
scale = mscalemax / currentscale; 
} 
msupportmatrix.postscale(scale, scale, centerx, centery); 
rectf boundary = getdrawboundary(getdrawmatrix()); 
float translatex = 0; 
if (boundary.left > mcroprect.left) { 
translatex = mcroprect.left - boundary.left; 
} else if (boundary.right < mcroprect.right) { 
translatex = mcroprect.right - boundary.right; 
} 
log.d("scale", "x==>" + translatex); 
float translatey = 0; 
if (boundary.top > mcroprect.top) { 
translatey = mcroprect.top - boundary.top; 
} else if (boundary.bottom < mcroprect.bottom) { 
translatey = mcroprect.bottom - boundary.bottom; 
} 
msupportmatrix.posttranslate(translatex, translatey); 
setimagematrix(getdrawmatrix()); 
return true; 
} 
protected matrix getdrawmatrix() { 
mdrawmatrix.reset(); 
if (mrotation % 360 != 0) { 
final boolean rotated = isimagerotated(); 
final int width = rotated ? mimageheight : mimagewidth; 
final int height = rotated ? mimagewidth : mimageheight; 
mdrawmatrix.postrotate(mrotation, mimagewidth / 2, mimageheight / 2); 
if (rotated) { 
final int translatex = (width - mimagewidth) / 2; 
final int translatey = (height - mimageheight) / 2; 
mdrawmatrix.posttranslate(translatex, translatey); 
} 
} 
mdrawmatrix.postconcat(mbasematrix); 
mdrawmatrix.postconcat(msupportmatrix); 
return mdrawmatrix; 
} 
@override 
public boolean onscalebegin(scalegesturedetector detector) { 
return true; 
} 
@override 
public void onscaleend(scalegesturedetector detector) { 
final float currentscale = getscale(msupportmatrix); 
if (currentscale < 1.0f) { 
log.e("onscaleend", "currentscale==>" + currentscale); 
rectf boundary = getdrawboundary(getdrawmatrix()); 
post(new animatedzoomrunnable(currentscale, 1.0f, boundary.centerx(), boundary.centery())); 
} 
} 
protected rectf getdrawboundary(matrix matrix) { 
drawable drawable = getdrawable(); 
if (drawable == null) { 
return mboundaryrect; 
} 
final int bitmapwidth = drawable.getintrinsicwidth(); 
final int bitmapheight = drawable.getintrinsicheight(); 
mboundaryrect.set(0, 0, bitmapwidth, bitmapheight); 
matrix.maprect(mboundaryrect); 
return mboundaryrect; 
} 
public float getscale(matrix matrix) { 
return (float) math.sqrt((float) math.pow(getvalue(matrix, matrix.mscale_x), 2) + (float) math.pow(getvalue(matrix, matrix.mskew_y), 2)); 
} 
/** 
* helper method that 'unpacks' a matrix and returns the required value 
* 
* @param matrix - matrix to unpack 
* @param whichvalue - which value from matrix.m* to return 
* @return float - returned value 
*/ 
private float getvalue(matrix matrix, int whichvalue) { 
matrix.getvalues(mmatrixvalues); 
return mmatrixvalues[whichvalue]; 
} 
public void enabledrawcropwidget(boolean enable) { 
menabledrawcropwidget = enable; 
} 
protected enum operation { 
drag, scale 
} 
public enum type { 
center_crop, center_inside 
} 
public interface idecodecallback { 
void ondecoded(final int rotation, final bitmap bitmap); 
} 
//setimagepath这个方法耗时,需要显示进度条,这个是监听 
public interface onbitmaploadlistener { 
void onloadprepare(); 
void onloadfinish(); 
} 
private class animatedzoomrunnable implements runnable { 
private final float mfocalx, mfocaly; 
private final long mstarttime; 
private final float mzoomstart, mzoomend; 
public animatedzoomrunnable(final float currentzoom, final float targetzoom, 
final float focalx, final float focaly) { 
mfocalx = focalx; 
mfocaly = focaly; 
mstarttime = system.currenttimemillis(); 
mzoomstart = currentzoom; 
mzoomend = targetzoom; 
} 
@override 
public void run() { 
float t = interpolate(); 
float scale = mzoomstart + t * (mzoomend - mzoomstart); 
float deltascale = scale / getscale(msupportmatrix); 
msupportmatrix.postscale(deltascale, deltascale, mfocalx, mfocaly); 
setimagematrix(getdrawmatrix()); 
// we haven't hit our target scale yet, so post ourselves again 
if (t < 1f) { 
postonanimation(this); 
} 
} 
private float interpolate() { 
float t = 1f * (system.currenttimemillis() - mstarttime) / 200; 
t = math.min(1f, t); 
t = sinterpolator.getinterpolation(t); 
return t; 
} 
} 
} 
<declare-styleable name="life_cropimage"> 
<attr name="life_crop_ratio" format="float" /> 
<attr name="life_crop_scale_type" format="enum"> 
<enum name="life_center_crop" value="0" /> 
<enum name="life_center_inside" value="1" /> 
</attr> 
</declare-styleable>

1、让这个裁剪框显示图片:

mseniorimageview.setimagepath(path);

2、保存裁剪后的图片:

bitmap imageviewbitmap = null; 
try { 
imageviewbitmap = mseniorimageview.savecrop(); 
} catch (outofmemoryerror e) { 
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(new seniorcropimageview.onbitmaploadlistener() { 
@override 
public void onloadprepare() { 
mactivity.showprogress(); 
} 
@override 
public void onloadfinish() { 
mactivity.hideprogress(); 
} 
});

以上所述是小编给大家带来的android 以任意比例裁剪图片代码分享,希望对大家有所帮助