Android achieves square avatar cropping
Realize the idea. The visible area of the interface is a 2-layer view
The top-most view is the display layer, which mainly draws a translucent border area and a white clipping area, which is easier to code.
The second layer inherits ImageView, uses the ImageView matrix to realize the display part picture, and moves, zooms in and so on the operation.
More complex is the effect of multi-finger operation on ImageView, see the code:
Clipsquareimageview.java
Package Com.h3c.androidclipsquare;import Android.annotation.targetapi;import Android.content.context;import Android.graphics.bitmap;import Android.graphics.canvas;import Android.graphics.matrix;import Android.graphics.rectf;import Android.graphics.drawable.drawable;import Android.os.build;import Android.util.attributeset;import Android.view.gesturedetector;import Android.view.motionevent;import Android.view.scalegesturedetector;import Android.view.velocitytracker;import Android.view.View;import Android.view.viewconfiguration;import Android.view.viewtreeobserver;import android.widget.imageview;/** * Created by H3C on 12/13/14. */public class Clipsquareimageview extends ImageView implements View.ontouchlistener, Viewtreeobserver.ongloballayoutlistener {private static final int borderdistance = clipsquareview.borderdistance; public static final Float Default_max_scale = 4.0f; public static final Float Default_mid_scale = 2.0f; public static final Float Default_min_scale = 1.0F private float Minscale = Default_min_scale; private float midscale = Default_mid_scale; private float Maxscale = Default_max_scale; Private Multigesturedetector Multigesturedetector; Private Boolean isiniting;//is initializing private matrix Defaultmatrix = new Matrix ();//initialized picture matrix, control picture full screen and display area private Mat Rix Dragmatrix = new Matrix ()//drag-and-drop the dynamic matrix in the process of the private matrix Finalmatrix = new Matrix ();//finally display the Matrix private final REC TF displayrect = new RECTF ();//The real size of the picture private final float[] matrixvalues = new FLOAT[9]; private int borderlength; Public Clipsquareimageview (context context, AttributeSet Attrs) {Super (context, attrs); Super.setscaletype (Scaletype.matrix); Setontouchlistener (this); Multigesturedetector = new Multigesturedetector (context); } @Override protected void Onattachedtowindow () {Super.onattachedtowindow (); Getviewtreeobserver (). Addongloballayoutlistener (this); } @SuppressWarnings ("Deprecation ") @Override protected void Ondetachedfromwindow () {Super.ondetachedfromwindow (); Getviewtreeobserver (). Removeglobalonlayoutlistener (this); } @Override public void Ongloballayout () {if (isiniting) {return; }//Adjust view position initbmpposition (); }/** * Initialize picture position */private void Initbmpposition () {isiniting = true; Super.setscaletype (Scaletype.matrix); drawable drawable = getdrawable (); if (drawable = = null) {return; } final Float viewwidth = getwidth (); Final float viewheight = GetHeight (); Final int drawablewidth = Drawable.getintrinsicwidth (); Final int drawableheight = Drawable.getintrinsicheight (); if (Viewwidth < viewheight) {borderlength = (int) (VIEWWIDTH-2 * borderdistance); } else {borderlength = (int) (VIEWHEIGHT-2 * borderdistance); } float Screenscale = 1f; Images smaller than the screen will be filled with screen if (Drawablewidth <= drawableheight) {//Portrait picture Screenscale = (float) borderlength/dr Awablewidth; } else {//Horizontal picture Screenscale = (float) borderlength/drawableheight; } defaultmatrix.setscale (Screenscale, Screenscale); if (drawablewidth <= drawableheight) {//Vertical picture float Heightoffset = (viewheight-drawableheight * screenscale) /2.0f; if (viewwidth <= viewheight) {//Vertical photo vertical screen defaultmatrix.posttranslate (borderdistance, Heightoffset); } else {//Vertical photo Horizontal screen defaultmatrix.posttranslate ((viewwidth-borderlength)/2.0f, heightoffset); }} else {float Widthoffset = (viewwidth-drawablewidth * screenscale)/2.0f; if (viewwidth <= viewheight) {//Horizontal photo, vertical screen defaultmatrix.posttranslate (Widthoffset, (Viewheight-borderlen GTH)/2.0f); } else {//horizontal photo, horizontal screen Defaultmatrix.posttranslAte (Widthoffset, borderdistance); }} resetmatrix (); }/** * Resets the Matrix back to Fit_center, and then displays IT.S */private void Resetmatrix () { if (Dragmatrix = = null) {return; } dragmatrix.reset (); Setimagematrix (Getdisplaymatrix ()); } private Matrix Getdisplaymatrix () {finalmatrix.set (Defaultmatrix); Finalmatrix.postconcat (Dragmatrix); return Finalmatrix; } @Override Public Boolean OnTouch (view view, Motionevent motionevent) {return Multigesturedetector.ontouche Vent (motionevent); } private class Multigesturedetector extends Gesturedetector.simpleongesturelistener implements Scalegesture Detector.onscalegesturelistener {private final scalegesturedetector scalegesturedetector; Private final Gesturedetector Gesturedetector; Private final float Scaledtouchslop; Private Velocitytracker Velocitytracker; PRivate Boolean isdragging; private float Lasttouchx; private float Lasttouchy; Private float lastpointercount;//Last time was a few fingers event public multigesturedetector (context context) {Scalegesture Detector = new Scalegesturedetector (context, this); Gesturedetector = new Gesturedetector (context, this); Gesturedetector.setondoubletaplistener (this); Final Viewconfiguration Configuration = viewconfiguration.get (context); Scaledtouchslop = Configuration.getscaledtouchslop (); } @Override public boolean onscale (Scalegesturedetector scalegesturedetector) {Float scale = get Scale (); float scalefactor = Scalegesturedetector.getscalefactor (); if (getdrawable () = null && ((Scale < Maxscale && scalefactor > 1.0f) | | (Scale > Minscale && scalefactor < 1.0f))) {if (Scalefactor * Scale < Minscale) {Scalefactor = Minscale/scale; } if (Scalefactor * scale > Maxscale) {scalefactor = Maxscale/scale; } dragmatrix.postscale (Scalefactor, Scalefactor, GetWidth ()/2, GetHeight ()/2); Checkanddisplaymatrix (); } return true; } @Override public boolean onscalebegin (Scalegesturedetector scalegesturedetector) {return true; } @Override public void Onscaleend (Scalegesturedetector scalegesturedetector) {} public Boole An ontouchevent (Motionevent event) {if (Gesturedetector.ontouchevent (event)) {return true; } scalegesturedetector.ontouchevent (event); /* Get the center x, y of all the pointers */float x = 0, y = 0; Final int pointercount = Event.getpointercount (); for (int i = 0; i < Pointercount; i++) { x + = Event.getx (i); Y + = event.gety (i); } x = X/pointercount; y = Y/pointercount; /* * If the pointer count has changed cancel the drag */if (pointercount! = Lastpointe Rcount) {isdragging = false; if (velocitytracker! = null) {velocitytracker.clear (); } LASTTOUCHX = x; Lasttouchy = y; Lastpointercount = Pointercount; } switch (Event.getaction ()) {Case MotionEvent.ACTION_DOWN:if (Velocitytrac Ker = = null) {Velocitytracker = Velocitytracker.obtain (); } else {velocitytracker.clear (); } velocitytracker.addmovement (event); Lasttouchx = x; Lasttouchy = y; ISdragging = false; Break Case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:lastPointerCount = 0; if (velocitytracker! = null) {velocitytracker.recycle (); Velocitytracker = null; } break; Case Motionevent.action_move: {final float dx = x-lasttouchx, dy = y-lasttouchy; if (isdragging = = False) {//use Pythagoras to see if drag length is larger than Touch Slop isdragging = math.sqrt (DX * dx) + (dy * dy)) >= scaledtouchslop; } if (isdragging) {if (getdrawable () = null) { Dragmatrix.posttranslate (dx, dy); Checkanddisplaymatrix (); } Lasttouchx = x; Lasttouchy = y; if (velocitytracker! = null) {velocitytracker.addmovement (event); }} break; }} return true; } @Override public boolean Ondoubletap (Motionevent event) {try {float scale = ge TScale (); float x = getwidth ()/2; Float y = getheight ()/2; if (Scale < Midscale) {post (the new animatedzoomrunnable (scale, Midscale, X, y)); } else if (scale >= Midscale) && (Scale < Maxscale)) {post (new animatedzoomrunnable (SCA Le, Maxscale, x, y)); } else {//double-click to zoom out less than the minimum value of post (new animatedzoomrunnable (scale, Minscale, X, y)); }} catch (Exception e) {//Can SometimEs happen when GetX () and GetY () are called} return true; }} Private class Animatedzoomrunnable implements Runnable {//These is ' postscale ' values, means they ' re C Ompounded each iteration static final float animation_scale_per_iteration_in = 1.07f; Static final float animation_scale_per_iteration_out = 0.93f; Private final float focalx, focaly; Private final float targetzoom; Private final float Deltascale; Public animatedzoomrunnable (final float currentzoom, final float targetzoom, final Floa T Focalx, final float focaly) {this.targetzoom = Targetzoom; This.focalx = Focalx; This.focaly = Focaly; if (Currentzoom < targetzoom) {Deltascale = animation_scale_per_iteration_in; } else {deltascale = animation_scale_per_iteration_out; }} @Override PubLIC void Run () {Dragmatrix.postscale (Deltascale, Deltascale, Focalx, focaly); Checkanddisplaymatrix (); Final float Currentscale = Getscale (); if (((Deltascale > 1f) && (Currentscale < targetzoom)) | | ((Deltascale < 1f) && (Targetzoom < Currentscale))) {//We haven ' t hits our target scale yet, so post ourselves//again Postonan Imation (Clipsquareimageview.this, this); } else {//we ' ve scaled past our target zoom, so calculate the//necessary scale so We ' re Back at Target zoom final float delta = targetzoom/currentscale; Dragmatrix.postscale (Delta, Delta, Focalx, Focaly); Checkanddisplaymatrix (); }}} @TargetApi (Build.version_codes. Jelly_bean) private void postonanimation (view view, Runnable Runnable) {if (Build.VERSION.SDK_INT ≫= Build.version_codes. Jelly_bean) {view.postonanimation (runnable); } else {view.postdelayed (runnable, 16); }}/** * Returns the current scale value * * @return float-current Scale value */public final Float Getscale () {dragmatrix.getvalues (matrixvalues); return matrixvalues[matrix.mscale_x]; }/** * Helper method that simply checks the Matrix, and then displays the result */private void Checkanddi Splaymatrix () {checkmatrixbounds (); Setimagematrix (Getdisplaymatrix ()); } private void Checkmatrixbounds () {final RECTF rect = Getdisplayrect (Getdisplaymatrix ()); if (null = = rect) {return; } float deltax = 0, DeltaY = 0; Final float viewwidth = getwidth (); Final float viewheight = GetHeight (); After inference is moved or scaled, the picture is displayed beyond the crop box boundary final float Heightborder = (viewheight-borderlength)/2; Final float weightBorder = (viewwidth-borderlength)/2; if (Rect.top > Heightborder) {deltay = Heightborder-rect.top; } if (Rect.bottom < (Viewheight-heightborder)) {deltay = Viewheight-heightborder-rect.bottom; } if (Rect.left > Weightborder) {deltax = Weightborder-rect.left; } if (Rect.right < Viewwidth-weightborder) {deltax = Viewwidth-weightborder-rect.right; }//Finally actually translate the matrix dragmatrix.posttranslate (DeltaX, DeltaY); }/** * Get pictures relative to matrix distance * * @param matrix *-matrix to map drawable against * @return R ectf-displayed Rectangle */private RECTF Getdisplayrect (Matrix matrix) {drawable d = getdrawable (); if (null! = d) {displayrect.set (0, 0, d.getintrinsicwidth (), D.getintrinsicheight ()); Matrix.maprect (Displayrect); return displayrect;} return null; /** * Cut the picture and return the clipped Bitmap object * * @return */public Bitmap clip () {Bitmap Bitmap = bitmap.creat Ebitmap (GetWidth (), GetHeight (), Bitmap.Config.ARGB_8888); Canvas canvas = new canvas (bitmap); Draw (canvas); Return Bitmap.createbitmap (Bitmap, (GetWidth ()-borderlength)/2, (GetHeight ()-borderlength)/2, Borderlength, border length); }}
Code Download: Https://github.com/h3clikejava/AndroidClipSquare
Androidclipsquare android for Square avatar cropping