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 以任意比例裁剪图片代码分享,希望对大家有所帮助
上一篇: MySQL中EXPLAIN命令详解
下一篇: java调用ffmpeg实现转换视频