Android gesture detection creates an image preview effect that supports scaling and translation (on ),
Reprinted please indicate the source: bytes]
1. Background
Now, the image preview function is indispensable in the app, and the user has basically formed a conditioned reflection. When you see a small image, click it to see the big image, and the two fingers start to zoom in, start to move to the specified part ~~~
I believe that the entire steps shown in the figure should not be interrupted ~~~ "I can't zoom in on it. What is it? unload it ~~ "," I wiped it and couldn't move it. Why? I unloaded it ~~ ".
Ha ~ Therefore, for image preview, we need to make users feel better. Secondly, as developers, we also need to know how to implement it ~~~
2. Overview
To support multi-touch images and freely scale and translate images, you need to know the following knowledge points: Matrix, GestureDetector, ScaleGestureDetector, and event distribution mechanism.
1. Matrix
Matrix. I 've got a deeper look at all the multiplication of a 3-dimensional matrix. It's really troublesome ~~
In fact, you can understand this:
Matrix
Data Structure: 3-dimensional matrix;
Internal Storage: New Float [9]; it is a one-dimensional array with nine elements in it; it can be initialized by setValues (float [] values ).
Meaning of each element:
{MSCALE_X, MSKEW_X, MTRANS_X, MSKEW_Y, MSCALE_Y, MTRANS_Y, MPERSP_0, MPERSP_1, MPERSP_2 };
Literally, we can see which one represents the zooming in the x direction and which one represents the offset in the vertical direction ~~ There are three that you don't know. It's okay. Please ignore it.
Operation
For example, you want to set the offset of matrix to 200,100.
You can write as follows:
Matrix transMatrix = new Matrix();float[] values = new float[] { 1.0, 0, 200, 0, 1.0, 100, 0, 0, 1.0 };transMatrix.setValues(values);If you need to rotate at 30 degrees, zoom in twice ~~
This writing is actually a strange problem ~~
Matrix provides some common APIs: for example, we can write this:
Matrix transMatrix = new Matrix();transMatrix.postTranslate(200, 100);
How to Get value:
Of course, we have performed various operations on a Matrix, namely postScale and postTranslate. How can we get the current scaling ratio:
As mentioned above, setValues can be initialized, so getValues can get the value of the current matrix, get a one-dimensional array with nine elements, and then obtain the corresponding value through the subscript.
For example, I want to know the current x-direction scaling ratio:
public final float getScale(){scaleMatrix.getValues(matrixValues);return matrixValues[Matrix.MSCALE_X];}
Okay, that's enough to know ~~
2. GestureDetector
Well, by looking at the API, you can capture long-pressed and double-clicked items. The usage will be shown in the example.
3. ScaleGestureDetector
Well, it's a bit like inherited, but it's not actually an independent class ~ Gesture used to detect scaling ~~~ In the example
3. Practice
For better understanding, I will develop each function independently and then integrate it together ~~ You can also learn how to use each API.
1. Free Scaling
Requirement: When an image is loaded, the image is centered in the screen; the image is width or height on the screen, which is reduced to the screen size; the image can be scaled out or out freely;
The code is not very long. paste the Code directly:
Package com. zhy. view; import android. content. context; import android. graphics. matrix; import android. graphics. drawable. drawable; import android. util. attributeSet; import android. util. log; import android. view. motionEvent; import android. view. scaleGestureDetector; import android. view. scaleGestureDetector. onScaleGestureListener; import android. view. view; import android. view. view. onTouchListener; import android. view. viewTreeObserver; import android. widget. imageView; public class ZoomImageView extends ImageView implements OnScaleGestureListener, OnTouchListener, ViewTreeObserver. onGlobalLayoutListener {private static final String TAG = ZoomImageView. class. getSimpleName (); public static final float SCALE_MAX = 4.0f;/*** scale ratio during initialization. If the image is wide or tall on the screen, this value will be less than 0 */private float initScale = 1.0f;/*** used to store 9 values of the matrix */private final float [] matrixValues = new float [9]; private boolean once = true;/*** scaling gesture detection */private ScaleGestureDetector mScaleGestureDetector = null; private final Matrix mScaleMatrix = new Matrix (); public ZoomImageView (Context context) {this (context, null);} public ZoomImageView (Context context, AttributeSet attrs) {super (context, attrs); super. setScaleType (ScaleType. MATRIX); mScaleGestureDetector = new ScaleGestureDetector (context, this); this. setOnTouchListener (this) ;}@ Overridepublic boolean onScale (ScaleGestureDetector detector) {float scale = getScale (); float scaleFactor = detector. getScaleFactor (); if (getDrawable () = null) return true;/*** zoom range control */if (scale <SCALE_MAX & scaleFactor> 1.0f) | (scale> initScale & scaleFactor <1.0f) {/*** maximum and minimum value judgment */if (scaleFactor * scale <initScale) {scaleFactor = initScale/scale ;} if (scaleFactor * scale> SCALE_MAX) {scaleFactor = SCALE_MAX/scale;}/*** set the scaling ratio */mScaleMatrix. postScale (scaleFactor, scaleFactor, getWidth ()/2, getHeight ()/2); setImageMatrix (mScaleMatrix);} return true;} @ Overridepublic boolean onScaleBegin (ScaleGestureDetector detector) {return true ;}@ Overridepublic void onScaleEnd (ScaleGestureDetector detector) {}@ Overridepublic boolean onTouch (View v, MotionEvent event) {return mScaleGestureDetector. onTouchEvent (event);}/*** get the current scaling ratio ** @ return */public final float getScale () {mScaleMatrix. getValues (matrixValues); return matrixValues [Matrix. MSCALE_X] ;}@ Overrideprotected void onAttachedToWindow () {super. onAttachedToWindow (); getViewTreeObserver (). addOnGlobalLayoutListener (this);} @ SuppressWarnings ("deprecation") @ Overrideprotected void onDetachedFromWindow () {super. onDetachedFromWindow (); getViewTreeObserver (). removeGlobalOnLayoutListener (this) ;}@ Overridepublic void onGlobalLayout () {if (once) {Drawable d = getDrawable (); if (d = null) return; Log. e (TAG, d. getIntrinsicWidth () + "," + d. getIntrinsicHeight (); int width = getWidth (); int height = getHeight (); // get the width and height of the image. int dw = d. getIntrinsicWidth (); int dh = d. getIntrinsicHeight (); float scale = 1.0f; // if the image width or height is greater than the screen size, scale it to the screen width or height if (dw> width & dh <= height) {scale = width * 1.0f/dw;} if (dh> height & dw <= width) {scale = height * 1.0f/dh;} // if both width and height are greater than the screen, adjust the screen size proportionally. if (dw> width & dh> height) {scale = Math. min (dw * 1.0f/width, dh * 1.0f/height);} initScale = scale; // move the image to the screen center mScaleMatrix. postTranslate (width-dw)/2, (height-dh)/2); mScaleMatrix. postScale (scale, scale, getWidth ()/2, getHeight ()/2); setImageMatrix (mScaleMatrix); once = false ;}}}
1. In the onGlobalLayout callback, the image is scaled and moved to the center of the Screen Based on the image width and height and the screen width and height. If the image is small, it will be displayed normally and will not be enlarged ~
2. We have the OnTouchListener MotionEvent handed over to ScaleGestureDetector for processing.
@Overridepublic boolean onTouch(View v, MotionEvent event){return mScaleGestureDetector.onTouchEvent(event);}
3. Adjust the image scaling in the onScale callback. First, judge the scaling range, and then set the scale value of mScaleMatrix.
Current results:
1. Smaller than the screen width and height
2. Greater than the screen width and height
The effect of real machine recording is not very good ~~ Join us ~
Now you can ~~~ Randomly zoomed in and out ~~~
Download source code
However, there are problems:
1. the center point of the scaling is fixed and the center of the screen is
2. The image cannot be moved after being enlarged ~
Next, let's first solve the problem of scaling the center point. Can't we keep pressing the center of the screen? Like me, I pay more attention to my sister's eyes. I want to zoom in on that one ~~~
2. Set the scaling Center
1. Set the scaling Center
The setting center is simple. You can directly modify the following center:
/*** Set the scaling ratio */mScaleMatrix. postScale (scaleFactor, scaleFactor, detector. getFocusX (), detector. getFocusX (); setImageMatrix (getDisplayMatrix ());
However, arbitrary center point enlargement or reduction will lead to changes in the image position. As a result, when the image width is larger than the screen, the image and the screen are white edges; when the image is smaller than the screen, but not centered.
2. Control the image display range During Scaling
Therefore, when scaling, We need to manually control the following range:
/*** Adjust the image display range During Scaling */private void checkBorderAndCenterWhenScale () {RectF rect = getMatrixRectF (); float deltaX = 0; float deltaY = 0; int width = getWidth (); int height = getHeight (); // if the width or height is greater than the screen, the control range is if (rect. width ()> = width) {if (rect. left> 0) {deltaX =-rect. left;} if (rect. right <width) {deltaX = width-rect. right ;}} if (rect. height ()> = height) {if (rect. top> 0) {deltaY =-rect. top;} if (rect. bott Om /*** Set the scaling ratio */mScaleMatrix. postScale (scaleFactor, scaleFactor, detector. getFocusX (), detector. getFocusY (); merge (); setImageMatrix (mScaleMatrix );
In this way, you can freely zoom in any place without any white edges on the boundary. The image can also be displayed in the middle of the screen (when the image width or height is smaller than the screen );
3. paste the layout File
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.zhy.view.ZoomImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="matrix" android:src="@drawable/xx" /></RelativeLayout>
The eyes are the windows of the soul. Let's zoom in and see ,:
Now, we can reduce the image size as needed ~~~ Completed ~~~ If you only need the zoom function, you can use it ~
Due to space reasons, the next article will continue to improve this View:
1. Move when multiple touch points are added
2. Increase double-click to enlarge and double-click to become smaller.
3. Event conflicts with ViewPager
Download source code
Certificate ---------------------------------------------------------------------------------------------------------------------------------------
Last Post advertisement:
The first video recording ~~~ Hope everyone can support and make progress together ~
High imitation 5.2.1 main interface and message reminder