在viewPager中雙指縮放圖片,雙擊縮放圖片,單指拖拽圖片

来源:http://www.cnblogs.com/tangZH/archive/2017/05/11/6839343.html
-Advertisement-
Play Games

我們就把這個問題叫做圖片查看器吧,它的主要功能有: 1、雙擊縮放圖片。 2、 雙指縮放圖片。 3、單指拖拽圖片。 為此這個圖片查看器需要考慮以下的技術點: 一、雙擊縮放圖片: 1、如果圖片高度比屏幕的高度小得多,那麼就將圖片放大到高度與屏幕高度相等,否則就放大一個特定的倍數。 2、如何判斷是否到達這 ...


 

 我們就把這個問題叫做圖片查看器吧,它的主要功能有:

1、雙擊縮放圖片。

2、 雙指縮放圖片。

3、單指拖拽圖片。

為此這個圖片查看器需要考慮以下的技術點:

一、雙擊縮放圖片:

1、如果圖片高度比屏幕的高度小得多,那麼就將圖片放大到高度與屏幕高度相等,否則就放大一個特定的倍數。

2、如何判斷是否到達這個倍數來停止縮放。

3、判斷完且停止放大後,圖片可能已經超出了這個倍數需要的大小,如何回歸到我們的目標大小。

4、判斷完且停止縮小後,圖片寬度可能已經小於屏幕寬度,在兩邊留下了空白,如何重置為原來的大小。

 

二、雙指縮放圖片:

1、雙指縮放,放大一個特定的倍數停止。

2、如何判斷是否到達這個倍數。

3、放大停止後,圖片可能已經超出了這個倍數需要的大小,如何回歸到我們的目標大小。

4、縮小停止後,圖片寬度可能已經小於屏幕寬度,在兩邊留下了空白,如何重置為原來的大小。

 

三、單指拖拽:

1、當圖片寬度小於或等於屏幕寬度的時候,禁止左右移動,當圖片的高度小於屏幕高度的時候,禁止上下移動。

2、移動圖片時,如果圖片的一邊已經與屏幕之間有了空白,鬆手後恢復,讓圖片的這一邊與屏幕邊界重合。

 

四、

如何判斷是雙擊,還是多指觸控,還是單指。

 

五、

如何解決與viewPager的滑動衝突,當圖片已經滑動到盡頭無法滑動時,此時viewPager應該攔截事件。

 

我們逐一來解決:

 

public class MyImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener,View.OnTouchListener {
 public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setScaleType(ScaleType.MATRIX);
        setOnTouchListener(this);
        /**
         * 雙擊實現圖片放大縮小
         */
        mGestureDetector = new GestureDetector(context,
                new GestureDetector.SimpleOnGestureListener() {
                    @Override
                    public boolean onDoubleTap(MotionEvent e) {
                        changeViewSize(e);
                        return true;
                    }
                });
    }

在這裡縮放圖片是用matrix,因此首先要設置scaleType為matrix。
用手勢判斷雙擊行為。不要忘了在onTouch裡面加上

if (mGestureDetector.onTouchEvent(event))
            return true;


判斷單指與多指觸控,則在onTouch裡面判斷,要用 event.getAction() & MotionEvent.ACTION_MASK來判斷。

//多指觸控模式,單指,雙指
private int mode;

private final static int SINGLE_TOUCH = 1; //單指
private final static int DOUBLE_TOUCH = 2; //雙指

 

@Override
    public boolean onTouch(View view, MotionEvent event) {
        rectF = getMatrixRectF();
        if (mGestureDetector.onTouchEvent(event))
            return true;

        switch (event.getAction() & event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mode = SINGLE_TOUCH;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode >= DOUBLE_TOUCH) //雙指縮放
                {
                }
                if (mode == SINGLE_TOUCH) //單指拖拽
                {
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                mode += 1;break;
            case MotionEvent.ACTION_POINTER_UP:
                mode -= 1;
                break;
            case MotionEvent.ACTION_UP:
                mode = 0;
                break;
            //在ACTION_MOVE中,事件被攔截了之後,有時候ACTION_UP無法觸發,所以加上了ACTION_CANCEL
            case MotionEvent.ACTION_CANCEL:
                mode = 0;
                break;
            default:
                break;
        }
        return true;
    }

 

有如下事件使我們要用到的:

  • MotionEvent.ACTION_DOWN:在第一個點被按下時觸發
  • MotionEvent.ACTION_UP:當屏幕上唯一的點被放開時觸發
  • MotionEvent.ACTION_POINTER_DOWN:當屏幕上已經有一個點被按住,此時再按下其他點時觸發。
  • MotionEvent.ACTION_POINTER_UP:當屏幕上有多個點被按住,鬆開其中一個點時觸發(即非最後一個點被放開時)。
  • MotionEvent.ACTION_MOVE:當有點在屏幕上移動時觸發。值得註意的是,由於它的靈敏度很高,而我們的手指又不可能完全靜止(即使我們感覺不到移動,但其實我們的手指也在不停地抖動),所以實際的情況是,基本上只要有點在屏幕上,此事件就會一直不停地被觸發。

在ACTION_MOVE中通過mode的大小來判斷是單指還是雙指。 

不過有一個令人傷心的事情,Android自己有一個bug。經過測試發現雙指交換觸碰圖片的時候,程式會閃退,出現異常:pointIndex out of range。這是Android自己的bug。個人覺得最好得解決方法是自定義一個viewPager,然後在裡面重寫:onTouchEvent,onInterceptTouchEvent,然後捕獲異常。

   @Override
    public boolean onTouchEvent(MotionEvent ev) {
        try {
            return super.onTouchEvent(ev);
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        try {
            return super.onInterceptTouchEvent(ev);
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        return false;
    }

這樣程式就不會閃退了。

 

我們來看看雙擊放大的的代碼:

    /**
     * 雙擊縮放圖片
     */
    private void changeViewSize(MotionEvent e) {
        
        //獲取雙擊的坐標
        final float x = e.getX();
        final float y = e.getY();

        //如果此時還在縮放那就直接返回
        if (animator != null && animator.isRunning())
            return;
        
        //判斷是處於放大還是縮小的狀態
        if (!isZoomChanged()) {
            animator = ValueAnimator.ofFloat(1.0f, 2.0f);
        } else {
            animator = ValueAnimator.ofFloat(1.0f, 0.0f);
        }
        animator.setTarget(this);
        animator.setDuration(500);
        animator.setInterpolator(new DecelerateInterpolator());
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

                Float value = (Float) animator.getAnimatedValue();
                matrix.postScale(value, value, x, y);
                checkBorderAndCenterWhenScale(); //在縮放後讓圖片居中
                setImageMatrix(matrix);

                /**
                 * 控制縮小的範圍
                 * 如果已經小於初始大小,那麼恢復到初始大小,然後停止
                 */
                if (checkRestScale()) {
                    matrix.set(oldMatrix);  //oldMatrix為最原始的matrix
                    setImageMatrix(matrix);
                    return;
                }
                /**
                 * 控制放大的範圍
                 * 如果已經大於目標的放大倍數,那麼直接置為目標的放大倍數
                 * 然後停止
                 */
                if (getMatrixValueX() >= mDoubleClickScale)
                {
                    matrix.postScale(mDoubleClickScale/getMatrixValueX(), mDoubleClickScale/getMatrixValueX(), x, y);
                    checkBorderAndCenterWhenScale();
                    setImageMatrix(matrix);
                    return;
                }
            }
        });
    }

 

 

判斷處於放大還是縮小狀態的代碼:(不是初始值就說明是處於放大狀態)

    /**
     * 判斷縮放級別是否是改變過
     *
     * @return true表示非初始值, false表示初始值
     */
    private boolean isZoomChanged() {
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        //獲取當前X軸縮放級別
        float scale = values[Matrix.MSCALE_X];
        //獲取初始時候的X軸縮放級別,兩者做比較
        oldMatrix.getValues(values);
        return scale != values[Matrix.MSCALE_X];
    }

 

getMatrixValue()的代碼如下,是為了取得當前的放大倍數,相對於一開始的圖片來說

 private float getMatrixValueX()
    {
        // TODO Auto-generated method stub
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        //獲取當前X軸縮放級別
        float scale = values[Matrix.MSCALE_X];
        //獲取原始Matrix的X軸縮放級別
        oldMatrix.getValues(values);
//返回放大的倍數
return scale / values[Matrix.MSCALE_X]; }

 

 

checkRestScale()的代碼如下,主要是為了判斷當前的縮放級別是否小於最初始的縮放級別。

    /**
     * 判斷是否需要重置
     *
     * @return 當前縮放級別小於原始縮放級別時,重置
     */
    private boolean checkRestScale() {
        // TODO Auto-generated method stub
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        //獲取當前X軸縮放級別
        float scale = values[Matrix.MSCALE_X];
        //獲取原始的X軸縮放級別,兩者做比較
        oldMatrix.getValues(values);
        return scale < values[Matrix.MSCALE_X];
    }

 

 

checkBorderAndCenterWhenScale()的代碼如下,否則圖片縮放後位置會發生變化。

/**
     * 在縮放時,進行圖片顯示範圍的控制
     */
    private void checkBorderAndCenterWhenScale()
    {

        RectF rect = getMatrixRectF();
        float deltaX = 0;
        float deltaY = 0;

        int width = getWidth();
        int height = getHeight();

        // 如果寬或高大於屏幕,則控制範圍
        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.bottom < height)
            {
                deltaY = height - rect.bottom;
            }
        }
        // 如果寬或高小於屏幕,則讓其居中
        if (rect.width() < width)
        {
            deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
        }
        if (rect.height() < height)
        {
            deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
        }

        matrix.postTranslate(deltaX, deltaY);
        setImageMatrix(matrix);
    }

 

接下來看看雙指縮放和單指拖拽:

@Override
    public boolean onTouch(View view, MotionEvent event) {
        rectF = getMatrixRectF(); //獲取圖片邊界範圍
        if (mGestureDetector.onTouchEvent(event))
            return true;

        switch (event.getAction() & event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                //如果放大後圖片的邊界超出了屏幕,那麼就攔截事件,不讓viewPager處理
                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                mode = SINGLE_TOUCH;

                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode >= DOUBLE_TOUCH) //雙指縮放
                {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    newDist = calculateDist(event); //計算距離
                    Point point = getMiPoint(event); //獲取兩手指間的中點坐標
                    if (newDist > oldDist + 1) //放大(加一是為了防止抖動)
                    {
                        changeViewSize(oldDist, newDist, point); //根據距離實現放大縮小
                        oldDist = newDist;
                    }
                    if (oldDist > newDist + 1) //縮小
                    {
                        changeViewSize(oldDist, newDist, point);
                        oldDist = newDist;
                    }
                }
                if (mode == SINGLE_TOUCH) //單指拖拽
                {
                    float dx = event.getRawX() - x;
                    float dy = event.getRawY() - y;

                    //如果移動過程中圖片的邊界超出了屏幕,那麼就攔截事件,不讓viewPager處理
                    if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                    //如果向右移動圖片到了盡頭,那麼就不要攔截事件,讓viewPager處理
                    if (rectF.left >= 0 && dx > 0)
                        getParent().requestDisallowInterceptTouchEvent(false);

                    //如果向左移動到了盡頭,那麼就不要攔截事件,讓viewPager處理
                    if (rectF.right <= getWidth() && dx < 0)
                        getParent().requestDisallowInterceptTouchEvent(false);

                    if (getDrawable() != null) {
                        //如果圖片寬度或高度沒有超出屏幕,那麼就禁止左右或上下滑動
                        if (rectF.width() <= getWidth())
                            dx = 0;
                        if (rectF.height() < getHeight())
                            dy = 0;

                        //如果圖片向下移動到了盡頭,不讓它繼續移動
                        if (rectF.top >= 0 && dy > 0)
                            dy = 0;
                        //如果圖片向上移動到了盡頭,不讓它繼續移動
                        if (rectF.bottom <= getHeight() && dy < 0)
                            dy = 0;

                        //當移動距離大於1的時候再移動,因為ACTION_MOVE比較靈敏,
                        // 手指即使只是放在上面,依然能夠檢測到手指的抖動,然後讓圖片移動。
                        if (Math.abs(dx) > 1 || Math.abs(dy) > 1)
                            matrix.postTranslate(dx, dy);
                        setImageMatrix(matrix);
                    }
                }
                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                mode += 1;
                oldDist = calculateDist(event); break;
            case MotionEvent.ACTION_POINTER_UP:
                mode -= 1;
                break;
            case MotionEvent.ACTION_UP:
                backToPosition();
                mode = 0;
                break;
            //在ACTION_MOVE中,事件被攔截了之後,有時候ACTION_UP無法觸發,所以加上了ACTION_CANCEL
            case MotionEvent.ACTION_CANCEL:
                backToPosition();
                mode = 0;
                break;
            default:
                break;
        }
        return true;
    }

 

 

首先先來看一個方法,根據圖片的matrix獲得圖片的邊界範圍,這個範圍映射在rect上。(這個範圍檢測是用在單指拖拽上的)

    /**
     * 根據當前圖片的Matrix獲得圖片的範圍
     *
     * @return
     */
    private RectF getMatrixRectF()
    {
        RectF rect = new RectF();
        Drawable d = getDrawable();
        if (null != d)
        {
            rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            matrix.mapRect(rect); 
} Log.e("aaaa",""+rect.bottom+" "+rect.left+" "+rect.right+" "+rect.top); return rect; }

rect.bottom:圖片下邊界的縱坐標

rect.left:圖片左邊界的橫坐標

rect.right:圖片右邊界的橫坐標

rect.top:圖片上邊界的縱坐標

rectF.width():圖片寬度

rectF.height():圖片高度 

需要註意的是Matrix對圖片的操作都是操作ImageView裡面的bitmap,ImageView是沒有變化的,上面所說的屏幕邊界其實ImageView的邊界,getWidth(),getHeight()是ImageView的寬和高。

 

方法 backToPosition()主要是實現單指拖拽的技術點2,當手指快速划過去的時候,在檢測到無法繼續滑動前圖片邊界與屏幕邊界已經出現了距離,所以鬆開手指的時候要複位,讓圖片邊界與屏幕邊界重合。

/**
     * 若是在移動後圖片的邊界脫離屏幕邊界,那麼就讓圖片邊界與屏幕邊界重合
     * 若手指快速移動,停止後會出現圖片距離屏幕有一段空白距離,然後經過判斷不能再移動,
     * 但是在進行下一次判斷是否可以繼續移動之前就已經出現了。
     * 所以需要複位
     */
    private void backToPosition() {
        if (rectF.left >= 0) { //圖片左邊界與屏幕出現距離
            matrix.postTranslate(-rectF.left, 0);
            setImageMatrix(matrix);
        }
        if (rectF.right <= getWidth()) { //圖片右邊界與屏幕出現距離
            matrix.postTranslate(getWidth() - rectF.right, 0);
            setImageMatrix(matrix);
        }
        if (rectF.top >= 0) { //圖片上邊界與屏幕出現距離
            matrix.postTranslate(0, -rectF.top);
            setImageMatrix(matrix);
        }
        if (rectF.bottom <= getHeight()) { //圖片下邊界與屏幕出現距離
            matrix.postTranslate(0, getHeight() - rectF.bottom);
            setImageMatrix(matrix);
        }
    }

 

獲取兩手指間的中點坐標

    /**
     * 獲取雙指縮放時候的縮放中點
     *
     * @return
     */
    private Point getMiPoint(MotionEvent event) {

        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);

        mPoint.set((int) x / 2, (int) y / 2);
        return mPoint;
    }

 

計算兩指觸摸點的距離

    /**
     * 計算兩指觸摸點之間的距離
     */
    private float calculateDist(MotionEvent event) {

        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);

    }

 

雙指縮放圖片

    /**
     * 雙指縮放圖片
     */
    private void changeViewSize(float oldDist, float newDist, Point mPoint) {
        float scale = newDist / oldDist; //縮放比例

        matrix.postScale(scale, scale, mPoint.x, mPoint.y);
        checkBorderAndCenterWhenScale();
        setImageMatrix(matrix);
        
        //防止縮小的時候小於初始的圖片大小,需要重置
        reSetMatrix();
        //如果縮放已經大於目標倍數,停止,因為有可能已經超出,那麼就直接縮放到目標大小
        if (getMatrixValueX() >= MAX_SCALE) 
        {
            matrix.postScale(MAX_SCALE/getMatrixValueX(), MAX_SCALE/getMatrixValueX(), x, y);
            checkBorderAndCenterWhenScale();
            setImageMatrix(matrix);
            return;
        }
    }

 

reSetMatrix()的代碼如下:

    /**
     * 重置Matrix
     */
    private void reSetMatrix() {
        if (checkRestScale()) {
            matrix.set(oldMatrix);
            setImageMatrix(matrix);
            return;
        }
    }

checkRestScale()的代碼在上面已經給出了。oldMatrix為最初始的Matrix。

 

到這裡還沒有結束,設置Imageview的ScaleType為Matrix,那麼圖片不會主動縮放到適應屏幕,也不會處於屏幕中間,因此我們的自定義ImageView需要繼承ViewTreeObserver.OnGlobalLayoutListener

 @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

 

 

 @Override
    public void onGlobalLayout() {
        if (once)
        {
            Drawable d = getDrawable();
            if (d == null)
                return;
            Log.e("TAG", d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());
            int width = getWidth();
            int height = getHeight();
            // 拿到圖片的寬和高
            int dw = d.getIntrinsicWidth();
            int dh = d.getIntrinsicHeight();
            float scale = 1.0f;
            // 如果圖片的寬或者高大於屏幕,則縮放至屏幕的寬或者高
            if (dw > width && dh <= height)
            {
                scale = width * 1.0f / dw;
            }
            if (dh > height && dw <= width)
            {
                scale = height * 1.0f / dh;
            }
            // 如果寬和高都大於屏幕,則讓其按按比例適應屏幕大小
            if (dw > width && dh > height)
            {
                scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
            }
            initScale = scale;

            Log.e("TAG", "initScale = " + initScale);
            matrix.postTranslate((width - dw) / 2, (height - dh) / 2);
            matrix.postScale(scale, scale, getWidth() / 2,
                    getHeight() / 2);
            // 圖片移動至屏幕中心
            setImageMatrix(matrix);

            oldMatrix.set(getImageMatrix());
            once = false;

            RectF rectF=getMatrixRectF();
            setDoubleClickScale(rectF);

        }
    }

 // 拿到圖片的寬和高
 int dw = d.getIntrinsicWidth();
 int dh = d.getIntrinsicHeight();
拿到的圖片寬和高是bitmap的真實高度。

初始的oldMatrix就是在這裡設置的,然後作為初始模板,代表著圖片沒被動手改變的Matrix。至於方法 setDoubleClickScale(rectF);只是設置雙擊放大的倍數而已,如果圖片高度比屏幕的高度小得多,那麼就將圖片放大到高度與屏幕高度相等,否則就放大一個特定的倍數。必須在這裡設置,因為在這裡取到的rectF才能反映原始圖片的邊界,因為這時候還沒有動手改變圖片。

    /**
     * 設置雙擊放大的倍數
     */
    private void setDoubleClickScale(RectF rectF)
    {
        if(rectF.height()<getHeight()-100)
        {
            mDoubleClickScale=getHeight()/rectF.height();
        }
        else
            mDoubleClickScale=2f;
    }

 

到這裡大概結束了,下麵就貼出完整的代碼:

package com.example.tangzh.myimageview;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;

/**
 * Created by TangZH on 2017/5/3.
 */
public class MyImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener,View.OnTouchListener {
    private final static int SINGLE_TOUCH = 1; //單指
    private final static int DOUBLE_TOUCH = 2; //雙指

    //多指觸控模式,單指,雙指
    private int mode;

    //兩指觸碰點之間的距離
    private float oldDist;
    private float newDist;

    /**
     * 最大縮放級別
     */
    private static final float MAX_SCALE = 5f;
    /**
     * 雙擊時的縮放級別
     */
    private  float mDoubleClickScale = 2;


    /**
     * 初始化時的縮放比例,如果圖片寬或高大於屏幕,此值將小於0
     */
    private float initScale = 1.0f;
    private boolean once = true;
    private RectF rectF;

    /**
     * 用於雙擊檢測
     */
    private GestureDetector mGestureDetector;
    private int x = 0;
    private int y = 0;

    private Point mPoint = new Point();

    private final Matrix matrix = new Matrix();
    private Matrix oldMatrix = new Matrix();

    private ValueAnimator animator;

    public MyImageView(Context context) {
        this(context, null);
    }

    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setScaleType(ScaleType.MATRIX);
        setOnTouchListener(this);
        /**
         * 雙擊實現圖片放大縮小
         */
        mGestureDetector = new GestureDetector(context,
                new GestureDetector.SimpleOnGestureListener() {
                    @Override
                    public boolean onDoubleTap(MotionEvent e) {
                        changeViewSize(e);
                        return true;
                    }
                });
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        rectF = getMatrixRectF(); //獲取圖片邊界範圍
        if (mGestureDetector.onTouchEvent(event))
            return true;

        switch (event.getAction() & event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                //如果放大後圖片的邊界超出了屏幕,那麼就攔截事件,不讓viewPager處理
                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                mode = SINGLE_TOUCH;

                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode >= DOUBLE_TOUCH) //雙指縮放
                {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    newDist = calculateDist(event); //計算距離
                    Point point = getMiPoint(event); //獲取兩手指間的中點坐標
                    if (newDist > oldDist + 1) //放大(加一是為了防止抖動)
                    {
                        changeViewSize(oldDist, newDist, point); //根據距離實現放大縮小
                        oldDist = newDist;
                    }
                    if (oldDist > newDist + 1) //縮小
                    {
                        changeViewSize(oldDist, newDist, point);
                        oldDist = newDist;
                    }
                }
                if (mode == SINGLE_TOUCH) //單指拖拽
                {
                    float dx = event.getRawX() - x;
                    float dy = event.getRawY() - y;

                    //如果移動過程中圖片的邊界超出了屏幕,那麼就攔截事件,不讓viewPager處理
                    if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                    //如果向右移動圖片到了盡頭,那麼就不要攔截事件,讓viewPager處理
                    if (rectF.left >= 0 && dx > 0)
                        getParent().requestDisallowInterceptTouchEvent(false);

                    //如果向左移動到了盡頭,那麼就不要攔截事件,讓viewPager處理
                    if (rectF.right <= getWidth() && dx < 0)
                        getParent().requestDisallowInterceptTouchEvent(false);

                    if (getDrawable() != null) {
                        //如果圖片寬度或高度沒有超出屏幕,那麼就禁止左右或上下滑動
                        if (rectF.width() <= getWidth())
                            dx = 0;
                        if (rectF.height() < getHeight())
                            dy = 0;

                        //如果圖片向下移動到了盡頭,不讓它繼續移動
                        if (rectF.top >= 0 && dy > 0)
                            dy = 0;
                        //如果圖片向上移動到了盡頭,不讓它繼續移動
                        if (rectF.bottom <= getHeight() && dy < 0)
                            dy = 0;

                        //當移動距離大於1的時候再移動,因為ACTION_MOVE比較靈敏,
                        // 手指即使只是放在上面,依然能夠檢測到手指的抖動,然後讓圖片移動。
                        if (Math.abs(dx) > 1 || Math.abs(dy) > 1)
                            matrix.postTranslate(dx, dy);
                        setImageMatrix(matrix);
                    }
                }
                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                mode += 1;
                oldDist = calculateDist(event);
                Log.e("q", "" + "a");

                Log.e(":::", "" + event.getPointerCount() + "   " + event.getActionIndex() + "   " + event.findPointerIndex(0));
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mode -= 1;
                break;
            case MotionEvent.ACTION_UP:
                backToPosition();
                mode = 0;
                break;
            //在ACTION_MOVE中,事件被攔截了之後,有時候ACTION_UP無法觸發,所以加上了ACTION_CANCEL
            case MotionEvent.ACTION_CANCEL:
                backToPosition();
                mode = 0;
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 計算兩指觸摸點之間的距離
     */
    private float calculateDist(MotionEvent event) {

        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在做手機端二次開發購物車的時候,發現zepto全選,沒找到,或者功能不是自己想要的 後來做好,分享給需要的人 //全選或多選處理 var CheckAll = $('#items_check_all'); var checkbox = $('input[name^="check"]'); var r ...
  • 一、面試80%都要問的數組去重 數組去重的方式有多種,其實面試中主要是想靠對對象的理解。還記得我第一次去面試的時候,去重的時候用了2個for迴圈。 二、返回字元串中字元出現次數最多的那字元 三、排序演算法(排序演算法的種類太多) 1.冒泡排序 2.選擇排序 三、不藉助中間變數交換2個變數的值 四、未完待 ...
  • 在JavaScript這門語言中,數據類型分為兩大類:基本數據類型和複雜數據類型。基本數據類型包括Number、Boolean、String、Null、String、Symbol(ES6 新增),而複雜數據類型包括Object,而所有其他引用類型(Array、Date、RegExp、Function ...
  • 學習nodejs三天,入了個門,感覺他和jsp、php還是存在較大的差別。本文首先複習這些天學的一些知識點,然後談一下如何一步一步到做一個線上實時填表的小應用,進一步鞏固一下這些個知識點。這裡先簡單介紹一下這個應用,班上經常要填一些電子統計表,要求每個人填對應的行,最後班長彙總出一個總表,這是非常麻... ...
  • css選擇器 在介紹之前我麽你先來看看css大致分為幾種選擇器: 1.類型選擇器(元素選擇器) 2.後代選擇器(元素的所有後代) 3.偽類(:active, :hover, :focus, :link, :visited, :first-child, :lang) 4.通用選擇器(*) 5.子選擇器 ...
  • XMLHttpRequest是一個瀏覽器介面,使得Javascript可以進行HTTP(S)通信。 最早,微軟在IE 5引進了這個介面。因為它太有用,其他瀏覽器也模仿部署了,ajax操作因此得以誕生。 但是,這個介面一直沒有標準化,每家瀏覽器的實現或多或少有點不同。HTML 5的概念形成後,W3C開 ...
  • node上傳圖片第一種方式 1,首先引入模塊 "connect-multiparty": "~1.2.5", 在package.json中添加 "connect-multiparty": "~1.2.5", 然後在命令中切換到項目目錄,使用npm命令:npm installl; 基本配置完成 ind ...
  • Array.from() 用於將兩類對象轉為真正的數組,類似數組對象和可遍歷對象(包括數據結構Set和Map)轉化為數組 格式:Array.from(arrayLike[, mapFn[, thisArg]]) // 字元串 Array.from('foo'); // ["f", "o", "o"] ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...