Touchimageview inherits all the functions of the ImageView with ImageView, in addition to scaling, drag, double-click Zoom, and other functions, support Viewpager and ScaleType, and accompanied by animation effect.
sharedconstructing
private void sharedconstructing (context context) {
super.setclickable (true);
This.context = context;
Mscaledetector = new Scalegesturedetector (context, new Scalelistener ());
Mgesturedetector = new Gesturedetector (context, new Gesturelistener ());
Matrix = new Matrix ();
Prevmatrix = new Matrix ();
m = new Float[9];
Normalizedscale = 1;
if (Mscaletype = = null) {
mscaletype = scaletype.fit_center;
}
Minscale = 1;
Maxscale = 3;
Superminscale = Super_min_multiplier * Minscale;
Supermaxscale = Super_max_multiplier * Maxscale;
Setimagematrix (matrix);
Setscaletype (Scaletype.matrix);
SetState (state.none);
Ondrawready = false;
Super.setontouchlistener (New Privateontouchlistener ());
}
Initialize, set Scalegesturedetector listener for Scalelistener, this is used to handle the indent drop potential, set Gesturedetector listener for Gesturelistener, This is used to handle double click and fling gestures, the first two gestures will cause the image to zoom, and fling will cause the picture to move.
Mscaledetector = new Scalegesturedetector (context, new Scalelistener ());
Mgesturedetector = new Gesturedetector (context, new Gesturelistener ());
Finally, the touch event listener for the custom view is Privateontouchlistener, which is the entrance to the touch event.
Super.setontouchlistener (New Privateontouchlistener ()); Privateontouchlistener Private class Privateontouchlistener implements Ontouchlistener {////Remember last point Positio
N for dragging//private PointF last = new PointF ();
@Override public boolean Ontouch (View V, motionevent event) {mscaledetector.ontouchevent (event);
Mgesturedetector.ontouchevent (event);
PointF Curr = new PointF (Event.getx (), event.gety ()); if (state = = State.none | | state = = State.drag | | | state = = state.fling) {switch (event.getaction ()) {case Motionevent.ac
TION_DOWN:last.set (Curr);
if (fling!= null) fling.cancelfling ();
SetState (State.drag);
Break Case MotionEvent.ACTION_MOVE:if (state = = State.drag) {float deltax = curr.x-last.x; float deltay = Curr.y-last.y; f
Loat fixtransx = Getfixdragtrans (DeltaX, Viewwidth, Getimagewidth ());
float Fixtransy = Getfixdragtrans (DeltaY, Viewheight, Getimageheight ());
Matrix.posttranslate (FIXTRANSX, Fixtransy);
Fixtrans ();
Last.set (curr.x, CURR.Y);
} break; CasE MotionEvent.ACTION_UP:case MotionEvent.ACTION_POINTER_UP:setState (state.none);
Break
} setimagematrix (matrix); user-defined Ontouchlistener//if (Usertouchlistener!= null) {Usertouchlistener.ontouch (V, event);}////Ontouch
Imageviewlistener is Set:touchimageview dragged by user. if (Touchimageviewlistener!= null) {Touchimageviewlistener.onmove ();}////indicate event is handled//return TRU
E }
}
Touch will go to Privateontouchlistener's Ontouch, It also gives the captured Motionevent to Mscaledetector and mgesturedetector to analyze whether there is a suitable callback function to handle the user's gestures.
Mscaledetector.ontouchevent (event);
Mgesturedetector.ontouchevent (event);
At the same time, when the current state is drag, the distance between x and Y is assigned to the transformation matrix.
Matrix.posttranslate (FIXTRANSX, Fixtransy);
Set the matrix to ImageView, complete the movement of x, Y, that is, to achieve the single finger drag action
Scalelistener
Private class Scalelistener extends Scalegesturedetector.simpleonscalegesturelistener {@Override public boolean Onscalebegin (Scalegesturedetector detector) {setstate (state.zoom); return true;} @Override public Boolean Onscale ( Scalegesturedetector detector) {scaleimage (Detector.getscalefactor (), Detector.getfocusx (), Detector.getFocusY (),
true);
Ontouchimageviewlistener is Set:touchimageview pinch zoomed by user.
if (Touchimageviewlistener!= null) {Touchimageviewlistener.onmove ();} return true;
@Override public void Onscaleend (Scalegesturedetector detector) {super.onscaleend (detector); SetState (State.none);
Boolean animatetozoomboundary = false;
float targetzoom = Normalizedscale; if (Normalizedscale > Maxscale) {targetzoom = Maxscale; animatetozoomboundary = true;} else if (Normalizedscale < Minscale) {targetzoom = Minscale; animatetozoomboundary = true;} if (animatetozoomboundary) {Doubletapzoom DoubleTap = New Doubletapzoom (Targetzoom, VIEWWIDTH/2, VIEWHEIGHT/2, True);
Compatpostonanimation (DOUBLETAP); }
}
}
The two-fingered scaling action goes to the Scalelistener callback, which handles the zoom of the picture in its Onscale callback
Scaleimage (Detector.getscalefactor (), Detector.getfocusx (), Detector.getfocusy (), true);
Scaleimage
private void Scaleimage (double deltascale, float focusx, float focusy, Boolean stretchimagetosuper) {
float lowerscal E, Upperscale;
if (stretchimagetosuper) {
lowerscale = Superminscale;
Upperscale = Supermaxscale;
} else {
Lowerscale = Minscale;
Upperscale = Maxscale;
}
float Origscale = Normalizedscale;
Normalizedscale *= Deltascale;
if (Normalizedscale > Upperscale) {
normalizedscale = Upperscale;
Deltascale = Upperscale/origscale;
} else if (Normalizedscale < Lowerscale) {
normalizedscale = Lowerscale;
Deltascale = Lowerscale/origscale;
}
Matrix.postscale (float) Deltascale, (float) Deltascale, Focusx, focusy);
Fixscaletrans ();
}
This will accumulate multiple scaling ratios and set a maximum and minimum zoom ratio, not exceeding the range
Normalizedscale *= Deltascale;
Finally, the scaling ratio and focus of x and Y are passed to the transformation matrix, which is connected to ImageView by matrix to complete the scaling action.
Matrix.postscale (float) Deltascale, (float) Deltascale, Focusx, focusy);
In the Onscaleend callback, we will determine whether the current scaling exceeds the maximum zoom ratio or less than the minimum scaling ratio, and if so, there will be an animation back to the maximum or minimum scaling state
Doubletapzoom Doubletap = new Doubletapzoom (Targetzoom, VIEWWIDTH/2, VIEWHEIGHT/2, true);
Compatpostonanimation (DOUBLETAP);
The animation doubletapzoom here is to double-click the animation, about Doubletapzoom we will talk about it below. Now that the two-fingered zoom action is complete, continue to see the double-click Zoom action.
Gesturelistener
Private class Gesturelistener extends Gesturedetector.simpleongesturelistener {@Override public boolean Onsingletapconfirmed (motionevent e) {if (Doubletaplistener!= null) {return doubletaplistener.onsingletapconfirmed (e)
;
return PerformClick (); @Override public void onlongpress (Motionevent e) {Performlongclick ():} @Override public boolean onfling (Motionevent E1 , motionevent E2, float Velocityx, float velocityy) {if (fling!= null) {////If a previous fling is still active, it s
Hould be cancelled so this two flings//are not run simultaenously.
Fling.cancelfling ();
} fling = new Fling ((int) Velocityx, (int) velocityy);
Compatpostonanimation (fling);
Return Super.onfling (E1, E2, Velocityx, Velocityy); @Override public boolean Ondoubletap (Motionevent e) {Boolean consumed = false; if (Doubletaplistener!= null) {Consumed
= Doubletaplistener.ondoubletap (e);
} if (state = = State.none) {Float Targetzoom = (Normalizedscale = = Minscale)? Maxscale:minscale; Doubletapzoom DOubletap = new Doubletapzoom (Targetzoom, E.getx (), E.gety (), false);
Compatpostonanimation (DOUBLETAP);
consumed = true;
return consumed; @Override public boolean ondoubletapevent (Motionevent e) {if (Doubletaplistener!= null) {return Doubletaplistener.ondo
Ubletapevent (e);
return false; }
}
In the Ondoubletap callback, set the double-click scaling ratio, or set the zoom ratio to the maximum if it is not currently scaled, or to no zoom if it is already the maximum value.
float Targetzoom = (Normalizedscale = = Minscale)? Maxscale:minscale;
Then the current click coordinates as the Zoom center, together with the scaling ratio to doubletapzoom, complete the zoom animation
Doubletapzoom Doubletap = new Doubletapzoom (Targetzoom, E.getx (), E.gety (), false);
Compatpostonanimation (DOUBLETAP);
Doubletapzoom
Private class Doubletapzoom implements Runnable {private long starttime; private static final float zoom_time =; pri
vate float startzoom, targetzoom;
Private float bitmapx, bitmapy;
Private Boolean stretchimagetosuper;
Private Acceleratedecelerateinterpolator interpolator = new Acceleratedecelerateinterpolator ();
Private PointF Starttouch;
Private PointF Endtouch; Doubletapzoom (float targetzoom, float focusx, float focusy, Boolean stretchimagetosuper) {SetState (state.animate_zoom)
;
StartTime = System.currenttimemillis ();
This.startzoom = Normalizedscale;
This.targetzoom = Targetzoom;
This.stretchimagetosuper = Stretchimagetosuper;
PointF bitmappoint = Transformcoordtouchtobitmap (Focusx, Focusy, false);
THIS.BITMAPX = Bitmappoint.x;
This.bitmapy = Bitmappoint.y;
Used for translating image during scaling//Starttouch = Transformcoordbitmaptotouch (bitmapx, bitmapy);
Endtouch = new PointF (VIEWWIDTH/2, VIEWHEIGHT/2); @Override public void Run () {Float t = interpolate ();
Double Deltascale = Calculatedeltascale (t);
Scaleimage (Deltascale, Bitmapx, Bitmapy, Stretchimagetosuper);
Translateimagetocentertouchposition (t);
Fixscaletrans ();
Setimagematrix (matrix);
Ontouchimageviewlistener is set:double tap runnable updates listener//with every frame. if (Touchimageviewlistener!= null) {Touchimageviewlistener.onmove ();} if (T < 1f) {////We haven ' t finished zoo
Ming//Compatpostonanimation (this);
else {////finished zooming//SetState (State.none);}} /** * Interpolate between where the image should start and end in order to translate * the
Touched is what ends up centered at the end * of the zoom. * @param t/private void translateimagetocentertouchposition (float t) {float targetx = starttouch.x + T * (endtouch.x-
starttouch.x);
Float targety = starttouch.y + t * (ENDTOUCH.Y-STARTTOUCH.Y);
PointF Curr = Transformcoordbitmaptotouch (bitmapx, bitmapy); Matrix.posttranslate (targetx-curr.x, Targety-CURR.Y); /** * Use Interpolator to get t * @return * * Private float interpolate () {Long currtime = System.currenttimemillis (); f
loat elapsed = (currtime-starttime)/zoom_time;
Elapsed = Math.min (1f, elapsed);
return interpolator.getinterpolation (elapsed);
}/** * Interpolate the current targeted zoom and get the delta * to the current zoom. * @param T * @return/private double Calculatedeltascale (float t) {Double zoom = startzoom + T * (Targetzoom-startzoo
m);
return zoom/normalizedscale; }
}
Doubletapzoom is actually a thread that implements the runnable, and we look directly at its run method, which defines a time t
float T = interpolate ();
In fact, T accelerates growth from 0 to 1 within 500ms through an accelerated differential
Private float interpolate () {
Long currtime = System.currenttimemillis ();
FLOAT elapsed = (currtime-starttime)/zoom_time;
Elapsed = Math.min (1f, elapsed);
return interpolator.getinterpolation (elapsed);
}
Calculates the current scaling ratio by T
Double Deltascale = Calculatedeltascale (t);
Implementing scaling
Scaleimage (Deltascale, Bitmapx, Bitmapy, Stretchimagetosuper);
It then determines whether the animation ends based on the value of the current T, or if T is less than 1, indicating that the animation is not finished, and that the setting state is completed before it is executed again. This is accomplished by redrawing the thread multiple times in the 500ms, ImageView the animation effect.
if (T < 1f) {
compatpostonanimation (this);
} else {
setstate (state.none);
}
At the same time, in the Gesturelistener onfling callback, set the X, Y speed of the fling, and then perform the fling displacement animation
Fling = new Fling ((int) Velocityx, (int) velocityy);
Compatpostonanimation (fling);
Fling
Private class Fling implements Runnable {Compatscroller scroller; int Currx, CurrY; Fling (int velocityx, int velocityy) {setstate (state.fling); scroller = new Compatscroller (context); Matrix.getvalues (m)
;
int startx = (int) m[matrix.mtrans_x];
int starty = (int) m[matrix.mtrans_y];
int MinX, MaxX, Miny, Maxy;
if (Getimagewidth () > viewwidth) {MinX = viewwidth-(int) getimagewidth (); MaxX = 0;} else {MinX = MaxX = StartX;} if (Getimageheight () > viewheight) {miny = viewheight-(int) getimageheight (); maxy = 0;} else {miny = Maxy = Star
TY;
Scroller.fling (StartX, starty, (int) Velocityx, (int) velocityy, MinX, MaxX, Miny, Maxy);
Currx = StartX;
CurrY = Starty; public void cancelfling () {if (scroller!= null) {setstate (state.none); scroller.forcefinished (true);} @Override PU Blic void Run () {////Ontouchimageviewlistener is Set:touchimageview listener has been flung by user.//Listener Runna
BLE updated with each frame of fling animation. if (TOUCHIMAGEVIEWLIstener!= null) {Touchimageviewlistener.onmove ();} if (Scroller.isfinished ()) {scroller = null; return;} if (scroller . Computescrolloffset ()) {int newx = SCROLLER.GETCURRX (); int newy = Scroller.getcurry (); int transx = newx-currx; int t
Ransy = Newy-curry;
Currx = newx;
CurrY = Newy;
Matrix.posttranslate (TRANSX, Transy);
Fixtrans ();
Setimagematrix (matrix);
Compatpostonanimation (this); }
}
}
Fling is actually a thread that implements the runnable, and according to the X and Y speed of the fling gesture we execute the Scroller fling function and set the current position to the starting position
Scroller.fling (StartX, starty, (int) Velocityx, (int) velocityy, Minx,maxx, Miny, Maxy);
Currx = StartX;
CurrY = Starty;
Then look at the run function, calculate the new position information according to the current scroll position of scroller, subtract from the old position, translate the distance between the X and Y axes, and realize the translation
if (Scroller.computescrolloffset ()) {
int newx = Scroller.getcurrx ();
int newy = Scroller.getcurry ();
int transx = Newx-currx;
int transy = Newy-curry;
Currx = newx;
CurrY = Newy;
Matrix.posttranslate (TRANSX, transy);
Fixtrans ();
Setimagematrix (matrix);
Compatpostonanimation (this);
}
The last delay for a period of time to call the thread to complete the new translation drawing, so reciprocating, until scroller stop scrolling, multiple redraw imageview to achieve the fling animation effect.
private void Compatpostonanimation (Runnable Runnable) {
if VERSION. Sdk_int >= version_codes. Jelly_bean) {
postonanimation (runnable);
} else {
postdelayed (runnable, 1000/60);
}
}
Let's take a look at the results:
Single picture
The picture is loaded into the Viewpager
Mirrored picture
Click to change the picture
Click to change ScaleType
The above is a small set to introduce the Android using ImageView to support gesture scaling effect, I hope to help you, if you have any questions please give me a message, small series will promptly reply to everyone. Here also thank you very much for the cloud Habitat Community website support!