Android gesture detection practices: creating an image preview that supports scaling and translation (bottom)

Source: Internet
Author: User
Tags gety

Android gesture detection practices: creating an image preview that supports scaling and translation (bottom)

Reprinted please indicate the source: bytes]

In the previous article, we introduced the Matrix with the help of freely zoomed in and out images. For more information, see Android gesture detection to create a preview effect for images that support zoom and translation (I ); this article continues to improve our ImageView ~~

First, add the enlarged movement ~~

1. freely move

We add the mobile code in onTouchEvent. Of course, the Code must be long or large enough to move the screen ~~~

@ Overridepublic boolean onTouch (View v, MotionEvent event) {mScaleGestureDetector. onTouchEvent (event); float x = 0, y = 0; // get the number of touch points final int pointerCount = event. getPointerCount (); // obtain the x and y mean of multiple touch points for (int I = 0; I <pointerCount; I ++) {x + = event. getX (I); y + = event. getY (I);} x = x/pointerCount; y = y/pointerCount;/*** reset mLasX, mLastY */if (pointerCount! = LastPointerCount) {isCanDrag = false; mLastX = x; mLastY = y;} lastPointerCount = pointerCount; switch (event. getAction () {case MotionEvent. ACTION_MOVE: Log. e (TAG, "ACTION_MOVE"); float dx = x-mLastX; float dy = y-mLastY; if (! IsCanDrag) {isCanDrag = isCanDrag (dx, dy);} if (isCanDrag) {RectF rectF = getMatrixRectF (); if (getDrawable ()! = Null) {isCheckLeftAndRight = isCheckTopAndBottom = true; // if the width is smaller than the screen width, it is prohibited to move left and right if (rectF. width () <getWidth () {dx = 0; isCheckLeftAndRight = false;} // if the height is light rain, move up or down is prohibited if (rectF. height () <getHeight () {dy = 0; isCheckTopAndBottom = false;} mScaleMatrix. postTranslate (dx, dy); checkMatrixBounds (); setImageMatrix (mScaleMatrix) ;}} mLastX = x; mLastY = y; break; case MotionEvent. ACTION_UP: case MotionEvent. ACTION_CANCEL: Log. e (TAG, "ACTION_UP"); lastPointerCount = 0; break;} return true ;}

First, we get the number of touch points, then obtain the average value of multiple touch points, set the mLastX and mLastY for us, and then get the dx and dy range check when moving, call mScaleMatrix. postTranslate is used to set the offset. Of course, after the setting is complete, you need to verify it again. You cannot move the image to a white edge on the screen boundary. After the verification is complete, you can call setImageMatrix.

Here: note that ACTION_DOWM is not rewritten because, when ACTION_DOWN is multi-touch, if one finger is pressed, other fingers will not trigger ACTION_DOWN again, however, after multiple fingers, the average value of the touch point will change greatly, so ACTION_DOWN is not used. Every time the number of touch points changes, we will follow the new current mLastX and mLastY.

The following are the two private methods used above. One is used to check the boundary and the other is used to determine whether the operation is a drag operation:

/*** When moving, perform boundary judgment, mainly to determine whether the width or height of the screen */private void checkMatrixBounds () {RectF rect = getMatrixRectF (); float deltaX = 0, deltaY = 0; final float viewWidth = getWidth (); final float viewHeight = getHeight (); // determine whether the image is displayed beyond the screen boundary if (rect. top> 0 & isCheckTopAndBottom) {deltaY =-rect. top;} if (rect. bottom <viewHeight & isCheckTopAndBottom) {deltaY = viewHeight-rect. bottom;} if (rect. left> 0 & isCheckLeftAndRight) {deltaX =-rect. left;} if (rect. right <viewWidth & isCheckLeftAndRight) {deltaX = viewWidth-rect. right;} mScaleMatrix. postTranslate (deltaX, deltaY);}/*** whether it is driving behavior ** @ param dx * @ param dy * @ return */private boolean isCanDrag (float dx, float dy) {return Math. sqrt (dx * dx) + (dy * dy)> = mTouchSlop ;}

In this way, we can zoom in, zoom out, and move ~~~

: This time I changed my man's picture, one of the protagonists of jailbreak, TBug ~


Our scaling and moving operations are complete ~~

2. Double-click zoom in and out

Speaking of double-click events, our GestureDetector is coming soon. This guy can capture double-click events ~~

1. Use of GestureDetector

Because GestureDetector sets the listener, the method is a large string, and we only need the onDoubleTap callback. Therefore, we are going to use its internal class SimpleOnGestureListener to implement empty implementations for other methods of the interface.

However, there are several questions that need to be discussed before we can start our code:

1. How do we change the double-click size?

Based on the current scaling value, if it is smaller than 2, we double-click it to change it to 2 times of the source image; if it is between 2 and 4, we double-click the source image four times, and the other status is four times. After double-click the source image, the original size is restored.

If you think this is inappropriate, you can adjust it according to your hobbies.

2. We need an animation when double-clicking the change ~~ For example, in the demo image in the previous example, the image is very large. When the screen is displayed, the value of initScale = 0.5 is left. If the double-click is changed to 2, that is, it is about four times larger in an instant and there is no transition effect, it makes the user feel very bad. Therefore, we are going to use postDelay to execute a Runnable, And the Runnable will continue to execute again based on the naturally scaled value.

First, complete the initialization of GestureDetector In the constructor and set the onDoubleTap listener.

public ZoomImageView(Context context, AttributeSet attrs){super(context, attrs);mScaleGestureDetector = new ScaleGestureDetector(context, this);mGestureDetector = new GestureDetector(context,new SimpleOnGestureListener(){@Overridepublic boolean onDoubleTap(MotionEvent e){if (isAutoScale == true)return true;float x = e.getX();float y = e.getY();Log.e("DoubleTap", getScale() + " , " + initScale);if (getScale() < SCALE_MID){ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MID, x, y), 16);isAutoScale = true;} else if (getScale() >= SCALE_MID&& getScale() < SCALE_MAX){ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MAX, x, y), 16);isAutoScale = true;} else{ZoomImageView.this.postDelayed(new AutoScaleRunnable(initScale, x, y), 16);isAutoScale = true;}return true;}});mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();super.setScaleType(ScaleType.MATRIX);this.setOnTouchListener(this);}

1. When double-clicking, first determine whether auto scaling is in progress. If auto scaling is in progress, directly retrun;

2. Then we enter our if. if of course the scale is smaller than 2, we will send a Runnable through view. for execution; others are similar;

Let's take a look at our Runnable code:

/*** Auto-scaling task ** @ author zhy **/private class AutoScaleRunnable implements Runnable {static final float BIGGER = 1.07f; static final float SMALLER = 0.93f; private float mTargetScale; private float tmpScale;/* zooming center */private float x; private float y;/*** input the target zoom value based on the target value and the current value, determine whether to zoom in or out ** @ param targetScale */public AutoScaleRunnable (float targetScale, float x, float y) {this. mTargetScale = targetScale; this. x = x; this. y = y; if (getScale () <mTargetScale) {tmpScale = BIGGER;} else {tmpScale = SMALLER ;}@ Overridepublic void run () {// scale mScaleMatrix. postScale (tmpScale, tmpScale, x, y); checkBorderAndCenterWhenScale (); setImageMatrix (mScaleMatrix); final float currentScale = getScale (); // if the value is within the valid range, continue scaling if (tmpScale> 1f) & (currentScale <mTargetScale) | (tmpScale <1f) & (mTargetScale <currentScale) {ZoomImageView. this. postDelayed (this, 16);} else // set the scale to the target {final float deltaScale = mTargetScale/currentScale; mScaleMatrix. postScale (deltaScale, deltaScale, x, y); checkBorderAndCenterWhenScale (); setImageMatrix (mScaleMatrix); isAutoScale = false ;}}}

After writing the code, we still need to pass our event to it, which is still in the onTouch method:

@Overridepublic boolean onTouch(View v, MotionEvent event){if (mGestureDetector.onTouchEvent(event))return true;

All right, double-click the zoom-in and zoom-out functions to complete the operation. Next, we will test ~~~

Finally, you can use the simulator ~~ :



3. handle conflicts with ViewPager

We directly use our images as ViewPager items. As you can imagine, there must be conflicts ~~

1. layout File

         
 

2. Activity Code

package com.zhy.zhy_scalegesturedetector02;import android.app.Activity;import android.os.Bundle;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import com.zhy.view.ZoomImageView;public class MainActivity extends Activity{private ViewPager mViewPager;private int[] mImgs = new int[] { R.drawable.tbug, R.drawable.a,R.drawable.xx };private ImageView[] mImageViews = new ImageView[mImgs.length];@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.vp);mViewPager = (ViewPager) findViewById(R.id.id_viewpager);mViewPager.setAdapter(new PagerAdapter(){@Overridepublic Object instantiateItem(ViewGroup container, int position){ZoomImageView imageView = new ZoomImageView(getApplicationContext());imageView.setImageResource(mImgs[position]);container.addView(imageView);mImageViews[position] = imageView;return imageView;}@Overridepublic void destroyItem(ViewGroup container, int position,Object object){container.removeView(mImageViews[position]);}@Overridepublic boolean isViewFromObject(View arg0, Object arg1){return arg0 == arg1;}@Overridepublic int getCount(){return mImgs.length;}});}}

Now we can run it directly and find that ViewPager is good, but after the image is enlarged, the Movement conflicts with the ViewPager and cannot be moved again ~... Wipe...

3. handling conflicts

Now let's think about it quickly. Remember to learn about the event distribution mechanism before. Our ZoomImageView is in ViewPager. If we don't want to be intercepted, what should we do?
First, we do not want to be intercepted if our width or height is higher than the screen width or height, because it can be moved at this time, we do not want to be intercepted. Next, do not want to be intercepted:

GetParent (). requestDisallowInterceptTouchEvent (true );

A line of code is sufficient. If you are not clear about blocking event distribution, refer to: How to not be blocked.

Put together our code is:

switch (event.getAction()){case MotionEvent.ACTION_DOWN:if (rectF.width() > getWidth() || rectF.height() > getHeight()){getParent().requestDisallowInterceptTouchEvent(true);}break;case MotionEvent.ACTION_MOVE:if (rectF.width() > getWidth() || rectF.height() > getHeight()){getParent().requestDisallowInterceptTouchEvent(true);}

~ When the screen width or height is higher than the screen width or height, the drag effect is regarded as moving the image, and vice versa, the ViewPager is used for processing.

Effect:


OK, now the conflict with ViewPager has been solved. ps: Nima should not be able to double-click twice or zoom in to four times...

4. Send the boundary event to ViewPager for processing.

Some users may want to send the event to ViewPager when the image reaches the boundary and cannot be dragged.

In ACTION_MOVE, the event is handed over to ViewPager when the current boundary has been reached and is still being pulled.

if (isCanDrag){if (getDrawable() != null){if (getMatrixRectF().left == 0 && dx > 0){getParent().requestDisallowInterceptTouchEvent(false);}if (getMatrixRectF().right == getWidth() && dx < 0){getParent().requestDisallowInterceptTouchEvent(false);}

Effect:


Okay. Actually, after this is added, the user experience is normal ~~~



After writing the code, there may be bugs in the code, problems found, or bugs fixed, I hope that I can leave a message directly below my blog, and make it easier for others ~~

At this point, our Android gesture detection practices have completed the process of creating an image preview that supports scaling and translation ~~!


We recommend that you double-click it to four times and comment out an If

//else if (getScale() >= SCALE_MID//&& getScale() < SCALE_MAX)//{//ZoomImageView.this.postDelayed(//new AutoScaleRunnable(SCALE_MAX, x, y), 16);//isAutoScale = true;//} 

Double-click and zoom in consecutively. It's uncomfortable. If the code has been uploaded, I will not re-upload it. If you feel uncomfortable, you can annotate it yourself.


Click to download the source code of a single image


Download ViewPager source code




Bytes -------------------------------------------------------------------------------------------------------

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












Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.