欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 在viewPager中雙指縮放圖片雙擊縮放圖片單指拖拽圖片的實現(xiàn)思路

 更新時間:2017年05月12日 08:49:46   作者:妖久  
本文通過實例代碼給大家講解了Android 在viewPager中雙指縮放圖片雙擊縮放圖片單指拖拽圖片的實現(xiàn)思路及解決方案,需要的朋友參考下吧

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

1、雙擊縮放圖片。

2、 雙指縮放圖片。

3、單指拖拽圖片。

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

一、雙擊縮放圖片:

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

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

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

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

二、雙指縮放圖片:

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

2、如何判斷是否到達這個倍數(shù)。

3、放大停止后,圖片可能已經(jīng)超出了這個倍數(shù)需要的大小,如何回歸到我們的目標大小。

4、縮小停止后,圖片寬度可能已經(jīng)小于屏幕寬度,在兩邊留下了空白,如何重置為原來的大小。

三、單指拖拽:

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

2、移動圖片時,如果圖片的一邊已經(jīng)與屏幕之間有了空白,松手后恢復(fù),讓圖片的這一邊與屏幕邊界重合。

四、

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

五、

如何解決與viewPager的滑動沖突,當圖片已經(jīng)滑動到盡頭無法滑動時,此時viewPager應(yīng)該攔截事件。

我們逐一來解決:

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);
  /**
   * 雙擊實現(xiàn)圖片放大縮小
   */
  mGestureDetector = new GestureDetector(context,
    new GestureDetector.SimpleOnGestureListener() {
     @Override
     public boolean onDoubleTap(MotionEvent e) {
      changeViewSize(e);
      return true;
     }
    });
 }

在這里縮放圖片是用matrix,因此首先要設(shè)置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無法觸發(fā),所以加上了ACTION_CANCEL
   case MotionEvent.ACTION_CANCEL:
    mode = 0;
    break;
   default:
    break;
  }
  return true;
 }

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

MotionEvent.ACTION_DOWN:在第一個點被按下時觸發(fā)

MotionEvent.ACTION_UP:當屏幕上唯一的點被放開時觸發(fā)

MotionEvent.ACTION_POINTER_DOWN:當屏幕上已經(jīng)有一個點被按住,此時再按下其他點時觸發(fā)。

MotionEvent.ACTION_POINTER_UP:當屏幕上有多個點被按住,松開其中一個點時觸發(fā)(即非最后一個點被放開時)。

MotionEvent.ACTION_MOVE:當有點在屏幕上移動時觸發(fā)。值得注意的是,由于它的靈敏度很高,而我們的手指又不可能完全靜止(即使我們感覺不到移動,但其實我們的手指也在不停地抖動),所以實際的情況是,基本上只要有點在屏幕上,此事件就會一直不停地被觸發(fā)。

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

不過有一個令人傷心的事情,Android自己有一個bug。經(jīng)過測試發(fā)現(xiàn)雙指交換觸碰圖片的時候,程序會閃退,出現(xiàn)異常: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;
  //判斷是處于放大還是縮小的狀態(tài)
  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);
    /**
     * 控制縮小的范圍
     * 如果已經(jīng)小于初始大小,那么恢復(fù)到初始大小,然后停止
     */
    if (checkRestScale()) {
     matrix.set(oldMatrix); //oldMatrix為最原始的matrix
     setImageMatrix(matrix);
     return;
    }
    /**
     * 控制放大的范圍
     * 如果已經(jīng)大于目標的放大倍數(shù),那么直接置為目標的放大倍數(shù)
     * 然后停止
     */
    if (getMatrixValueX() >= mDoubleClickScale)
    {
     matrix.postScale(mDoubleClickScale/getMatrixValueX(), mDoubleClickScale/getMatrixValueX(), x, y);
     checkBorderAndCenterWhenScale();
     setImageMatrix(matrix);
     return;
    }
   }
  });
 }

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

 /**
  * 判斷縮放級別是否是改變過
  *
  * @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()的代碼如下,是為了取得當前的放大倍數(shù),相對于一開始的圖片來說

 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);
  //返回放大的倍數(shù)
  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()的代碼如下,否則圖片縮放后位置會發(fā)生變化。

/**
  * 在縮放時,進行圖片顯示范圍的控制
  */
 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); //根據(jù)距離實現(xiàn)放大縮小
      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;
      //如果圖片向下移動到了盡頭,不讓它繼續(xù)移動
      if (rectF.top >= 0 && dy > 0)
       dy = 0;
      //如果圖片向上移動到了盡頭,不讓它繼續(xù)移動
      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無法觸發(fā),所以加上了ACTION_CANCEL
   case MotionEvent.ACTION_CANCEL:
    backToPosition();
    mode = 0;
    break;
   default:
    break;
  }
  return true;
 }

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

/**
  * 根據(jù)當前圖片的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()主要是實現(xiàn)單指拖拽的技術(shù)點2,當手指快速劃過去的時候,在檢測到無法繼續(xù)滑動前圖片邊界與屏幕邊界已經(jīng)出現(xiàn)了距離,所以松開手指的時候要復(fù)位,讓圖片邊界與屏幕邊界重合。

/**
  * 若是在移動后圖片的邊界脫離屏幕邊界,那么就讓圖片邊界與屏幕邊界重合
  * 若手指快速移動,停止后會出現(xiàn)圖片距離屏幕有一段空白距離,然后經(jīng)過判斷不能再移動,
  * 但是在進行下一次判斷是否可以繼續(xù)移動之前就已經(jīng)出現(xiàn)了。
  * 所以需要復(fù)位
  */
 private void backToPosition() {
  if (rectF.left >= 0) { //圖片左邊界與屏幕出現(xiàn)距離
   matrix.postTranslate(-rectF.left, 0);
   setImageMatrix(matrix);
  }
  if (rectF.right <= getWidth()) { //圖片右邊界與屏幕出現(xiàn)距離
   matrix.postTranslate(getWidth() - rectF.right, 0);
   setImageMatrix(matrix);
  }
  if (rectF.top >= 0) { //圖片上邊界與屏幕出現(xiàn)距離
   matrix.postTranslate(0, -rectF.top);
   setImageMatrix(matrix);
  }
  if (rectF.bottom <= getHeight()) { //圖片下邊界與屏幕出現(xiàn)距離
   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();
  //如果縮放已經(jīng)大于目標倍數(shù),停止,因為有可能已經(jīng)超出,那么就直接縮放到目標大小
  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()的代碼在上面已經(jīng)給出了。oldMatrix為最初始的Matrix。

到這里還沒有結(jié)束,設(shè)置Imageview的ScaleType為Matrix,那么圖片不會主動縮放到適應(yīng)屏幕,也不會處于屏幕中間,因此我們的自定義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;
   }
   // 如果寬和高都大于屏幕,則讓其按按比例適應(yīng)屏幕大小
   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就是在這里設(shè)置的,然后作為初始模板,代表著圖片沒被動手改變的Matrix。至于方法 setDoubleClickScale(rectF);只是設(shè)置雙擊放大的倍數(shù)而已,如果圖片高度比屏幕的高度小得多,那么就將圖片放大到高度與屏幕高度相等,否則就放大一個特定的倍數(shù)。必須在這里設(shè)置,因為在這里取到的rectF才能反映原始圖片的邊界,因為這時候還沒有動手改變圖片。

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

到這里大概結(jié)束了,下面就貼出完整的代碼:

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);
  /**
   * 雙擊實現(xiàn)圖片放大縮小
   */
  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); //根據(jù)距離實現(xiàn)放大縮小
      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;
      //如果圖片向下移動到了盡頭,不讓它繼續(xù)移動
      if (rectF.top >= 0 && dy > 0)
       dy = 0;
      //如果圖片向上移動到了盡頭,不讓它繼續(xù)移動
      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無法觸發(fā),所以加上了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);
  return (float) Math.sqrt(x * x + y * y);
 }
 @Override
 protected void onAttachedToWindow() {
  super.onAttachedToWindow();
  getViewTreeObserver().addOnGlobalLayoutListener(this);
 }
 /**
  * 若是在移動后圖片的邊界脫離屏幕邊界,那么就讓圖片邊界與屏幕邊界重合
  * 若手指快速移動,停止后會出現(xiàn)圖片距離屏幕有一段空白距離,然后經(jīng)過判斷不能再移動,
  * 但是在進行下一次判斷是否可以繼續(xù)移動之前就已經(jīng)出現(xiàn)了。
  * 所以需要復(fù)位
  */
 private void backToPosition() {
  if (rectF.left >= 0) { //圖片左邊界與屏幕出現(xiàn)距離
   matrix.postTranslate(-rectF.left, 0);
   setImageMatrix(matrix);
  }
  if (rectF.right <= getWidth()) { //圖片右邊界與屏幕出現(xiàn)距離
   matrix.postTranslate(getWidth() - rectF.right, 0);
   setImageMatrix(matrix);
  }
  if (rectF.top >= 0) { //圖片上邊界與屏幕出現(xiàn)距離
   matrix.postTranslate(0, -rectF.top);
   setImageMatrix(matrix);
  }
  if (rectF.bottom <= getHeight()) { //圖片下邊界與屏幕出現(xiàn)距離
   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 void changeViewSize(float oldDist, float newDist, Point mPoint) {
  float scale = newDist / oldDist; //縮放比例
  matrix.postScale(scale, scale, mPoint.x, mPoint.y);
  checkBorderAndCenterWhenScale();
  setImageMatrix(matrix);
  //防止縮小的時候小于初始的圖片大小,需要重置
  reSetMatrix();
  //如果縮放已經(jīng)大于目標倍數(shù),停止,因為有可能已經(jīng)超出,那么就直接縮放到目標大小
  if (getMatrixValueX() >= MAX_SCALE)
  {
   matrix.postScale(MAX_SCALE/getMatrixValueX(), MAX_SCALE/getMatrixValueX(), x, y);
   checkBorderAndCenterWhenScale();
   setImageMatrix(matrix);
   return;
  }
 }
 /**
  * 雙擊縮放圖片
  */
 private void changeViewSize(MotionEvent e) {
  //獲取雙擊的坐標
  final float x = e.getX();
  final float y = e.getY();
  //如果此時還在縮放那就直接返回
  if (animator != null && animator.isRunning())
   return;
  //判斷是處于放大還是縮小的狀態(tài)
  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);
    /**
     * 控制縮小的范圍
     * 如果已經(jīng)小于初始大小,那么恢復(fù)到初始大小,然后停止
     */
    if (checkRestScale()) {
     matrix.set(oldMatrix);
     setImageMatrix(matrix);
     return;
    }
    /**
     * 控制放大的范圍
     * 如果已經(jīng)大于目標的放大倍數(shù),那么直接置為目標的放大倍數(shù)
     * 然后停止
     */
    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];
 }
 /**
  * 重置Matrix
  */
 private void reSetMatrix() {
  if (checkRestScale()) {
   matrix.set(oldMatrix);
   setImageMatrix(matrix);
   return;
  }
 }
 /**
  * 設(shè)置雙擊放大的倍數(shù)
  */
 private void setDoubleClickScale(RectF rectF)
 {
  if(rectF.height()<getHeight()-100)
  {
   mDoubleClickScale=getHeight()/rectF.height();
  }
  else
   mDoubleClickScale=2f;
 }
 /**
  * 判斷是否需要重置
  *
  * @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];
 }
 private float getMatrixValueX()
 {
  // 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];
 }
 /**
  * 在縮放時,進行圖片顯示范圍的控制
  */
 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();
  }
  Log.e("TAG", "deltaX = " + deltaX + " , deltaY = " + deltaY);
  matrix.postTranslate(deltaX, deltaY);
  setImageMatrix(matrix);
 }
 /**
  * 根據(jù)當前圖片的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的輸出將會與上一句的一樣。
  }
  Log.e("aaaa",""+rect.bottom+" "+rect.left+" "+rect.right+" "+rect.top);
  return rect;
 }
 @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;
   }
   // 如果寬和高都大于屏幕,則讓其按按比例適應(yīng)屏幕大小
   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);
  }
 }
}

唉,雖然已經(jīng)寫完了,但是還有一個問題沒有解決,就是移動圖片到盡頭,這時候不要放手,往反方向移動,就會出現(xiàn)一個問題,圖片反方向的部分被遮擋,無法看到,然后移動的時候是直接切換圖片,而不再繼續(xù)移動圖片,這個問題的原因是:當你移動圖片到盡頭時,就把事件交給viewpager來處理了,即使再往反方向移動圖片,viewPager也一樣繼續(xù)攔截了事件。目前沒解決。

以上所述是小編給大家介紹的在viewPager中雙指縮放圖片雙擊縮放圖片單指拖拽圖片的實現(xiàn)思路,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Android中HttpURLConnection類使用介紹

    Android中HttpURLConnection類使用介紹

    早些時候其實我們都習(xí)慣性使用HttpClient,但是后來Android6.0之后不再支持HttpClient,需要添加Apache的jar才行,所以,就有很多開發(fā)者放棄使用HttpClient了,HttpURLConnection畢竟是標準Java接口(java.net) ,適配性還是很強的
    2022-12-12
  • Kotlin?select使用方法介紹

    Kotlin?select使用方法介紹

    select是Kotlin?1.6中的特性,即選擇最快的結(jié)果。select與async、Channel結(jié)合使用,可以大大提高程序的響應(yīng)速度,還可以提高程序的靈活性、擴展性
    2022-11-11
  • 搭建簡易藍牙定位系統(tǒng)的實現(xiàn)方法

    搭建簡易藍牙定位系統(tǒng)的實現(xiàn)方法

    下面小編就為大家?guī)硪黄罱ê喴姿{牙定位系統(tǒng)的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • Android開發(fā)實現(xiàn)帶有反彈效果仿IOS反彈scrollview教程詳解

    Android開發(fā)實現(xiàn)帶有反彈效果仿IOS反彈scrollview教程詳解

    本文給大家分享android開發(fā)實現(xiàn)帶有反彈效果,模仿ios反彈scrollview詳細教程,本文介紹的非常詳細,具有參考借鑒價值,感興趣的朋友一起看看吧
    2016-09-09
  • Android中home鍵和back鍵區(qū)別實例分析

    Android中home鍵和back鍵區(qū)別實例分析

    這篇文章主要介紹了Android中home鍵和back鍵區(qū)別,以實例形式較為詳細的分析并總結(jié)了home鍵和back鍵區(qū)別及使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-09-09
  • Android點擊事件的實現(xiàn)方式

    Android點擊事件的實現(xiàn)方式

    這篇文章主要為大家詳細介紹了Android點擊事件的實現(xiàn)方式,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Android實現(xiàn)倒計時30分鐘功能

    Android實現(xiàn)倒計時30分鐘功能

    這篇文章主要為大家詳細介紹了Android實現(xiàn)倒計時30分鐘功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Android自定義控件仿ios下拉回彈效果

    Android自定義控件仿ios下拉回彈效果

    這篇文章主要為大家詳細介紹了Android自定義控件仿ios下拉回彈效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Android WebViewClient 的 `shouldOverrideUrlLoading` 方法詳解

    Android WebViewClient 的 `shouldOverrideUrlLoa

    這篇文章主要介紹了Android WebViewClient 的 shouldOverrideUrlLoading方法,了解并正確實現(xiàn) WebViewClient 中的 shouldOverrideUrlLoading 方法對于在你的 Android 應(yīng)用中提供順暢且安全的瀏覽體驗至關(guān)重要,需要的朋友可以參考下
    2024-07-07
  • Android拍照裁剪圖片

    Android拍照裁剪圖片

    智能手機像素非常高,完全可以當相機使用,下面一段代碼給大家分享了android拍照裁剪圖片的功能,對android拍照裁剪圖片相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12

最新評論