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

Android多點觸控實現(xiàn)對圖片放大縮小平移,慣性滑動等功能

 更新時間:2016年02月17日 17:00:41   作者:阿拉燈神燈  
這篇文章主要介紹了Android多點觸控實現(xiàn)對圖片放大縮小平移,慣性滑動等功能的相關(guān)資料,需要的朋友可以參考下

文章將在原有基礎(chǔ)之上做了一些擴(kuò)展功能:

1.圖片的慣性滑動
2.圖片縮放小于正常比例時,松手會自動回彈成正常比例
3.圖片縮放大于最大比例時,松手會自動回彈成最大比例

實現(xiàn)圖片的縮放,平移,雙擊縮放等基本功能的代碼如下,每一行代碼我都做了詳細(xì)的注釋

public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,
  View.OnTouchListener , ViewTreeObserver.OnGlobalLayoutListener{
 /**
  * 縮放手勢的監(jiān)測
  */
 private ScaleGestureDetector mScaleGestureDetector;
 /**
  * 監(jiān)聽手勢
  */
 private GestureDetector mGestureDetector;
 /**
  * 對圖片進(jìn)行縮放平移的Matrix
  */
 private Matrix mScaleMatrix;
 /**
  * 第一次加載圖片時調(diào)整圖片縮放比例,使圖片的寬或者高充滿屏幕
  */
 private boolean mFirst;
 /**
  * 圖片的初始化比例
  */
 private float mInitScale;
 /**
  * 圖片的最大比例
  */
 private float mMaxScale;
 /**
  * 雙擊圖片放大的比例
  */
 private float mMidScale;

 /**
  * 是否正在自動放大或者縮小
  */
 private boolean isAutoScale;

 //-----------------------------------------------
 /**
  * 上一次觸控點的數(shù)量
  */
 private int mLastPointerCount;
 /**
  * 是否可以拖動
  */
 private boolean isCanDrag;
 /**
  * 上一次滑動的x和y坐標(biāo)
  */
 private float mLastX;
 private float mLastY;
 /**
  * 可滑動的臨界值
  */
 private int mTouchSlop;
 /**
  * 是否用檢查左右邊界
  */
 private boolean isCheckLeftAndRight;
 /**
  * 是否用檢查上下邊界
  */
 private boolean isCheckTopAndBottom;


 public ZoomImageView(Context context) {
  this(context, null, 0);
 }

 public ZoomImageView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  //一定要將圖片的ScaleType設(shè)置成Matrix類型的
  setScaleType(ScaleType.MATRIX);
  //初始化縮放手勢監(jiān)聽器
  mScaleGestureDetector = new ScaleGestureDetector(context,this);
  //初始化矩陣
  mScaleMatrix = new Matrix();
  setOnTouchListener(this);
  mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  //初始化手勢檢測器,監(jiān)聽雙擊事件
  mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){
   @Override
   public boolean onDoubleTap(MotionEvent e) {
    //如果是正在自動縮放,則直接返回,不進(jìn)行處理
    if (isAutoScale) return true;
    //得到點擊的坐標(biāo)
    float x = e.getX();
    float y = e.getY();
    //如果當(dāng)前圖片的縮放值小于指定的雙擊縮放值
    if (getScale() < mMidScale){
     //進(jìn)行自動放大
     post(new AutoScaleRunnable(mMidScale,x,y));
    }else{
     //當(dāng)前圖片的縮放值大于初試縮放值,則自動縮小
     post(new AutoScaleRunnable(mInitScale,x,y));
    }
    return true;
   }
  });



 }

 /**
  * 當(dāng)view添加到window時調(diào)用,早于onGlobalLayout,因此可以在這里注冊監(jiān)聽器
  */
 @Override
 protected void onAttachedToWindow() {
  super.onAttachedToWindow();
  getViewTreeObserver().addOnGlobalLayoutListener(this);
 }

 /**
  * 當(dāng)view從window上移除時調(diào)用,因此可以在這里移除監(jiān)聽器
  */
 @Override
 protected void onDetachedFromWindow() {
  super.onDetachedFromWindow();
  getViewTreeObserver().removeGlobalOnLayoutListener(this);
 }

 /**
  * 當(dāng)布局樹發(fā)生變化時會調(diào)用此方法,我們可以在此方法中獲得控件的寬和高
  */
 @Override
 public void onGlobalLayout() {
  //只有當(dāng)?shù)谝淮渭虞d圖片的時候才會進(jìn)行初始化,用一個變量mFirst控制
  if (!mFirst){
   mFirst = true;
   //得到控件的寬和高
   int width = getWidth();
   int height = getHeight();
   //得到當(dāng)前ImageView中加載的圖片
   Drawable d = getDrawable();
   if(d == null){//如果沒有圖片,則直接返回
    return;
   }
   //得到當(dāng)前圖片的寬和高,圖片的寬和高不一定等于控件的寬和高
   //因此我們需要將圖片的寬和高與控件寬和高進(jìn)行判斷
   //將圖片完整的顯示在屏幕中
   int dw = d.getIntrinsicWidth();
   int dh = d.getIntrinsicHeight();
   //我們定義一個臨時變量,根據(jù)圖片與控件的寬高比例,來確定這個最終縮放值
   float scale = 1.0f;
   //如果圖片寬度大于控件寬度,圖片高度小于控件高度
   if (dw>width && dh<height){
    //我們需要將圖片寬度縮小,縮小至控件的寬度
    //至于為什么要這樣計算,我們可以這樣想
    //我們調(diào)用matrix.postScale(scale,scale)時,寬和高都要乘以scale的
    //當(dāng)前我們的圖片寬度是dw,dw*scale=dw*(width/dw)=width,這樣就等于控件寬度了
    //我們的高度同時也乘以scale,這樣能夠保證圖片的寬高比不改變,圖片不變形
    scale = width * 1.0f / dw;

   }
   //如果圖片的寬度小于控件寬度,圖片高度大于控件高度
   if (dw<width && dh>height){
    //我們就應(yīng)該將圖片的高度縮小,縮小至控件的高度,計算方法同上
    scale = height * 1.0f / dh;
   }
   //如果圖片的寬度小于控件寬度,高度小于控件高度時,我們應(yīng)該將圖片放大
   //比如圖片寬度是控件寬度的1/2 ,圖片高度是控件高度的1/4
   //如果我們將圖片放大4倍,則圖片的高度是和控件高度一樣了,但是圖片寬度就超出控件寬度了
   //因此我們應(yīng)該選擇一個最小值,那就是將圖片放大2倍,此時圖片寬度等于控件寬度
   //同理,如果圖片寬度大于控件寬度,圖片高度大于控件高度,我們應(yīng)該將圖片縮小
   //縮小的倍數(shù)也應(yīng)該為那個最小值
   if ((dw < width && dh < height) || (dw > width && dh > height)){
    scale = Math.min(width * 1.0f / dw , height * 1.0f / dh);
   }

   //我們還應(yīng)該對圖片進(jìn)行平移操作,將圖片移動到屏幕的居中位置
   //控件寬度的一半減去圖片寬度的一半即為圖片需要水平移動的距離
   //高度同理,大家可以畫個圖看一看
   int dx = width/2 - dw/2;
   int dy = height/2 - dh/2;
   //對圖片進(jìn)行平移,dx和dy分別表示水平和豎直移動的距離
   mScaleMatrix.postTranslate(dx, dy);
   //對圖片進(jìn)行縮放,scale為縮放的比例,后兩個參數(shù)為縮放的中心點
   mScaleMatrix.postScale(scale, scale, width / 2, height / 2);
   //將矩陣作用于我們的圖片上,圖片真正得到了平移和縮放
   setImageMatrix(mScaleMatrix);

   //初始化一下我們的幾個縮放的邊界值
   mInitScale = scale;
   //最大比例為初始比例的4倍
   mMaxScale = mInitScale * 4;
   //雙擊放大比例為初始化比例的2倍
   mMidScale = mInitScale * 2;
  }
 }

 /**
  * 獲得圖片當(dāng)前的縮放比例值
  */
 private float getScale(){
  //Matrix為一個3*3的矩陣,一共9個值
  float[] values = new float[9];
  //將Matrix的9個值映射到values數(shù)組中
  mScaleMatrix.getValues(values);
  //拿到Matrix中的MSCALE_X的值,這個值為圖片寬度的縮放比例,因為圖片高度
  //的縮放比例和寬度的縮放比例一致,我們?nèi)∫粋€就可以了
  //我們還可以 return values[Matrix.MSCALE_Y];
  return values[Matrix.MSCALE_X];
 }

 /**
  * 獲得縮放后圖片的上下左右坐標(biāo)以及寬高
  */
 private RectF getMatrixRectF(){
  //獲得當(dāng)錢圖片的矩陣
  Matrix matrix = mScaleMatrix;
  //創(chuàng)建一個浮點類型的矩形
  RectF rectF = new RectF();
  //得到當(dāng)前的圖片
  Drawable d = getDrawable();
  if (d != null){
   //使這個矩形的寬和高同當(dāng)前圖片一致
   rectF.set(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
   //將矩陣映射到矩形上面,之后我們可以通過獲取到矩陣的上下左右坐標(biāo)以及寬高
   //來得到縮放后圖片的上下左右坐標(biāo)和寬高
   matrix.mapRect(rectF);
  }
  return rectF;
 }

 /**
  * 當(dāng)縮放時檢查邊界并且使圖片居中
  */
 private void checkBorderAndCenterWhenScale(){
  if (getDrawable() == null){
   return;
  }
  //初始化水平和豎直方向的偏移量
  float deltaX = 0.0f;
  float deltaY = 0.0f;
  //得到控件的寬和高
  int width = getWidth();
  int height = getHeight();
  //拿到當(dāng)前圖片對應(yīng)的矩陣
  RectF rectF = getMatrixRectF();
  //如果當(dāng)前圖片的寬度大于控件寬度,當(dāng)前圖片處于放大狀態(tài)
  if (rectF.width() >= width){
   //如果圖片左邊坐標(biāo)是大于0的,說明圖片左邊離控件左邊有一定距離,
   //左邊會出現(xiàn)一個小白邊
   if (rectF.left > 0){
    //我們將圖片向左邊移動
    deltaX = -rectF.left;
   }
   //如果圖片右邊坐標(biāo)小于控件寬度,說明圖片右邊離控件右邊有一定距離,
   //右邊會出現(xiàn)一個小白邊
   if (rectF.right <width){
    //我們將圖片向右邊移動
    deltaX = width - rectF.right;
   }
  }
  //上面是調(diào)整寬度,這是調(diào)整高度
  if (rectF.height() >= height){
   //如果上面出現(xiàn)小白邊,則向上移動
   if (rectF.top > 0){
    deltaY = -rectF.top;
   }
   //如果下面出現(xiàn)小白邊,則向下移動
   if (rectF.bottom < height){
    deltaY = height - rectF.bottom;
   }
  }
  //如果圖片的寬度小于控件的寬度,我們要對圖片做一個水平的居中
  if (rectF.width() < width){
   deltaX = width/2f - rectF.right + rectF.width()/2f;
  }

  //如果圖片的高度小于控件的高度,我們要對圖片做一個豎直方向的居中
  if (rectF.height() < height){
   deltaY = height/2f - rectF.bottom + rectF.height()/2f;
  }
  //將平移的偏移量作用到矩陣上
  mScaleMatrix.postTranslate(deltaX, deltaY);
 }

 /**
  * 平移時檢查上下左右邊界
  */
 private void checkBorderWhenTranslate() {
  //獲得縮放后圖片的相應(yīng)矩形
  RectF rectF = getMatrixRectF();
  //初始化水平和豎直方向的偏移量
  float deltaX = 0.0f;
  float deltaY = 0.0f;
  //得到控件的寬度
  int width = getWidth();
  //得到控件的高度
  int height = getHeight();
  //如果是需要檢查左和右邊界
  if (isCheckLeftAndRight){
   //如果左邊出現(xiàn)的白邊
   if (rectF.left > 0){
    //向左偏移
    deltaX = -rectF.left;
   }
   //如果右邊出現(xiàn)的白邊
   if (rectF.right < width){
    //向右偏移
    deltaX = width - rectF.right;
   }
  }
  //如果是需要檢查上和下邊界
  if (isCheckTopAndBottom){
   //如果上面出現(xiàn)白邊
   if (rectF.top > 0){
    //向上偏移
    deltaY = -rectF.top;
   }
   //如果下面出現(xiàn)白邊
   if (rectF.bottom < height){
    //向下偏移
    deltaY = height - rectF.bottom;
   }
  }

  mScaleMatrix.postTranslate(deltaX,deltaY);
 }


 /**
  * 自動放大縮小,自動縮放的原理是使用View.postDelay()方法,每隔16ms調(diào)用一次
  * run方法,給人視覺上形成一種動畫的效果
  */
 private class AutoScaleRunnable implements Runnable{
  //放大或者縮小的目標(biāo)比例
  private float mTargetScale;
  //可能是BIGGER,也可能是SMALLER
  private float tempScale;
  //放大縮小的中心點
  private float x;
  private float y;
  //比1稍微大一點,用于放大
  private final float BIGGER = 1.07f;
  //比1稍微小一點,用于縮小
  private final float SMALLER = 0.93f;
  //構(gòu)造方法,將目標(biāo)比例,縮放中心點傳入,并且判斷是要放大還是縮小
  public AutoScaleRunnable(float targetScale , float x , float y){
   this.mTargetScale = targetScale;
   this.x = x;
   this.y = y;
   //如果當(dāng)前縮放比例小于目標(biāo)比例,說明要自動放大
   if (getScale() < mTargetScale){
    //設(shè)置為Bigger
    tempScale = BIGGER;
   }
   //如果當(dāng)前縮放比例大于目標(biāo)比例,說明要自動縮小
   if (getScale() > mTargetScale){
    //設(shè)置為Smaller
    tempScale = SMALLER;
   }
  }
  @Override
  public void run() {
   //這里縮放的比例非常小,只是稍微比1大一點或者比1小一點的倍數(shù)
   //但是當(dāng)每16ms都放大或者縮小一點點的時候,動畫效果就出來了
   mScaleMatrix.postScale(tempScale, tempScale, x, y);
   //每次將矩陣作用到圖片之前,都檢查一下邊界
   checkBorderAndCenterWhenScale();
   //將矩陣作用到圖片上
   setImageMatrix(mScaleMatrix);
   //得到當(dāng)前圖片的縮放值
   float currentScale = getScale();
   //如果當(dāng)前想要放大,并且當(dāng)前縮放值小于目標(biāo)縮放值
   //或者 當(dāng)前想要縮小,并且當(dāng)前縮放值大于目標(biāo)縮放值
   if ((tempScale > 1.0f) && currentScale < mTargetScale
     ||(tempScale < 1.0f) && currentScale > mTargetScale){
    //每隔16ms就調(diào)用一次run方法
    postDelayed(this,16);
   }else {
    //current*scale=current*(mTargetScale/currentScale)=mTargetScale
    //保證圖片最終的縮放值和目標(biāo)縮放值一致
    float scale = mTargetScale / currentScale;
    mScaleMatrix.postScale(scale, scale, x, y);
    checkBorderAndCenterWhenScale();
    setImageMatrix(mScaleMatrix);
    //自動縮放結(jié)束,置為false
    isAutoScale = false;
   }
  }
 }

 /**
  * 這個是OnScaleGestureListener中的方法,在這個方法中我們可以對圖片進(jìn)行放大縮小
  */
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
  //當(dāng)我們兩個手指進(jìn)行分開操作時,說明我們想要放大,這個scaleFactor是一個稍微大于1的數(shù)值
  //當(dāng)我們兩個手指進(jìn)行閉合操作時,說明我們想要縮小,這個scaleFactor是一個稍微小于1的數(shù)值
  float scaleFactor = detector.getScaleFactor();
  //獲得我們圖片當(dāng)前的縮放值
  float scale = getScale();
  //如果當(dāng)前沒有圖片,則直接返回
  if (getDrawable() == null){
   return true;
  }
  //如果scaleFactor大于1,說明想放大,當(dāng)前的縮放比例乘以scaleFactor之后小于
  //最大的縮放比例時,允許放大
  //如果scaleFactor小于1,說明想縮小,當(dāng)前的縮放比例乘以scaleFactor之后大于
  //最小的縮放比例時,允許縮小
  if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxScale)
    || scaleFactor < 1.0f && scale * scaleFactor > mInitScale){
   //邊界控制,如果當(dāng)前縮放比例乘以scaleFactor之后大于了最大的縮放比例
   if (scale * scaleFactor > mMaxScale + 0.01f){
    //則將scaleFactor設(shè)置成mMaxScale/scale
    //當(dāng)再進(jìn)行matrix.postScale時
    //scale*scaleFactor=scale*(mMaxScale/scale)=mMaxScale
    //最后圖片就會放大至mMaxScale縮放比例的大小
    scaleFactor = mMaxScale / scale;
   }
   //邊界控制,如果當(dāng)前縮放比例乘以scaleFactor之后小于了最小的縮放比例
   //我們不允許再縮小
   if (scale * scaleFactor < mInitScale + 0.01f){
    //計算方法同上
    scaleFactor = mInitScale / scale;

   }
   //前兩個參數(shù)是縮放的比例,是一個稍微大于1或者稍微小于1的數(shù),形成一個隨著手指放大
   //或者縮小的效果
   //detector.getFocusX()和detector.getFocusY()得到的是多點觸控的中點
   //這樣就能實現(xiàn)我們在圖片的某一處局部放大的效果
   mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
   //因為圖片的縮放點不是圖片的中心點了,所以圖片會出現(xiàn)偏移的現(xiàn)象,所以進(jìn)行一次邊界的檢查和居中操作
   checkBorderAndCenterWhenScale();
   //將矩陣作用到圖片上
   setImageMatrix(mScaleMatrix);
  }
  return true;
 }

 /**
  * 一定要返回true
  */
 @Override
 public boolean onScaleBegin(ScaleGestureDetector detector) {
  return true;
 }

 @Override
 public void onScaleEnd(ScaleGestureDetector detector) {

 }

 @Override
 public boolean onTouch(View v, MotionEvent event) {
  //當(dāng)雙擊操作時,不允許移動圖片,直接返回true
  if (mGestureDetector.onTouchEvent(event)){
   return true;
  }
  //將事件傳遞給ScaleGestureDetector
  mScaleGestureDetector.onTouchEvent(event);
  //用于存儲多點觸控產(chǎn)生的坐標(biāo)
  float x = 0.0f;
  float y = 0.0f;
  //得到多點觸控的個數(shù)
  int pointerCount = event.getPointerCount();
  //將所有觸控點的坐標(biāo)累加起來
  for(int i=0 ; i<pointerCount ; i++){
   x += event.getX(i);
   y += event.getY(i);
  }
  //取平均值,得到的就是多點觸控后產(chǎn)生的那個點的坐標(biāo)
  x /= pointerCount;
  y /= pointerCount;
  //如果觸控點的數(shù)量變了,則置為不可滑動
  if (mLastPointerCount != pointerCount){
   isCanDrag = false;
   mLastX = x;
   mLastY = y;
  }
  mLastPointerCount = pointerCount;
  RectF rectF = getMatrixRectF();
  switch (event.getAction()){
   case MotionEvent.ACTION_DOWN:
    isCanDrag = false;
    //當(dāng)圖片處于放大狀態(tài)時,禁止ViewPager攔截事件,將事件傳遞給圖片,進(jìn)行拖動
    if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f){
     if (getParent() instanceof ViewPager){
      getParent().requestDisallowInterceptTouchEvent(true);
     }
    }
    break;
   case MotionEvent.ACTION_MOVE:
    //當(dāng)圖片處于放大狀態(tài)時,禁止ViewPager攔截事件,將事件傳遞給圖片,進(jìn)行拖動
    if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f){
     if (getParent() instanceof ViewPager){
      getParent().requestDisallowInterceptTouchEvent(true);
     }
    }
    //得到水平和豎直方向的偏移量
    float dx = x - mLastX;
    float dy = y - mLastY;
    //如果當(dāng)前是不可滑動的狀態(tài),判斷一下是否是滑動的操作
    if (!isCanDrag){
     isCanDrag = isMoveAction(dx,dy);
    }
    //如果可滑動
    if (isCanDrag){
     if (getDrawable() != null){
      isCheckLeftAndRight = true;
      isCheckTopAndBottom = true;
      //如果圖片寬度小于控件寬度
      if (rectF.width() < getWidth()){
       //左右不可滑動
       dx = 0;
       //左右不可滑動,也就不用檢查左右的邊界了
       isCheckLeftAndRight = false;
      }
      //如果圖片的高度小于控件的高度
      if (rectF.height() < getHeight()){
       //上下不可滑動
       dy = 0;
       //上下不可滑動,也就不用檢查上下邊界了
       isCheckTopAndBottom = false;
      }
     }
     mScaleMatrix.postTranslate(dx,dy);
     //當(dāng)平移時,檢查上下左右邊界
     checkBorderWhenTranslate();
     setImageMatrix(mScaleMatrix);
    }
    mLastX = x;
    mLastY = y;
    break;
   case MotionEvent.ACTION_UP:
    //當(dāng)手指抬起時,將mLastPointerCount置0,停止滑動
    mLastPointerCount = 0;
    break;
   case MotionEvent.ACTION_CANCEL:
    break;
  }
  return true;
 }


 /**
  * 判斷是否是移動的操作
  */
 private boolean isMoveAction(float dx , float dy){
  //勾股定理,判斷斜邊是否大于可滑動的一個臨界值
  return Math.sqrt(dx*dx + dy*dy) > mTouchSlop;
 }
}

實現(xiàn)圖片縮小后,松手回彈的效果

實現(xiàn)這個功能很簡單,我們先添加一個mMinScale作為可縮小到的最小值,我們指定為初試比例的1/4

 /**
  * 最小縮放比例
  */
 private float mMinScale;
//在onGlobalLayout中進(jìn)行初始化

 @Override
 public void onGlobalLayout() {
 ...
 //最小縮放比例為初試比例的1/4倍
 mMinScale = mInitScale / 4;
 ...
 }
//在onScale中,修改如下代碼

 @Override
 public boolean onScale(ScaleGestureDetector detector) {
 ...
  if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxScale)
    || scaleFactor < 1.0f && scale * scaleFactor > mMinScale){

   //邊界控制,如果當(dāng)前縮放比例乘以scaleFactor之后小于了最小的縮放比例
   //我們不允許再縮小
   if (scale * scaleFactor < mMinScale + 0.01f){

    scaleFactor = mMinScale / scale;
   }
 ...
 }

這樣我們的圖片最小就可以縮放到初始化比例的1/4大小了,然后我們還需要添加一個松手后回彈至初試化大小的動畫效果,然后我們需要在onTouch的ACTION_UP中添加如下代碼

 @Override
 public boolean onTouch(View v, MotionEvent event) {
 ...
  case MotionEvent.ACTION_UP:
    //當(dāng)手指抬起時,將mLastPointerCount置0,停止滑動
    mLastPointerCount = 0;
    //如果當(dāng)前圖片大小小于初始化大小
    if (getScale() < mInitScale){
     //自動放大至初始化大小
     post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));
    }
    break;
 ...
 }

現(xiàn)在我們看一下效果

實現(xiàn)圖片放大后,松手回彈效果

這個功能實現(xiàn)起來和上面那個功能基本一致,大家可以先試著自己寫一下。
同理,我們需要先定義一個mMaxOverScale作為放大到最大值后,還能繼續(xù)放大到的值。

/**
  * 最大溢出值
  */
 private float mMaxOverScale;
//在onGlobalLayout中進(jìn)行初始化

 @Override
 public void onGlobalLayout() {
 ...
 //最大溢出值為最大值的5倍,可以隨意調(diào)
 mMaxOverScale = mMaxScale * 5;
 ...
 }
//在onScale中,修改如下代碼

 @Override
 public boolean onScale(ScaleGestureDetector detector) {
 ...
  if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxOverScale)
    || scaleFactor < 1.0f && scale * scaleFactor > mMinScale){

   if (scale * scaleFactor > mMaxOverScale + 0.01f){

    scaleFactor = mMaxOverScale / scale;
   }
 ...
 }

這樣當(dāng)我們圖片放大至最大比例后還可以繼續(xù)放大,然后我們同樣需要在onTouch中的ACTION_UP中添加自動縮小的功能

 case MotionEvent.ACTION_UP:
    //當(dāng)手指抬起時,將mLastPointerCount置0,停止滑動
    mLastPointerCount = 0;
    //如果當(dāng)前圖片大小小于初始化大小
    if (getScale() < mInitScale){
     //自動放大至初始化大小
     post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));
    }
    //如果當(dāng)前圖片大小大于最大值
    if (getScale() > mMaxScale){
     //自動縮小至最大值
     post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2));
    }
    break;

然后我們看一下效果

實現(xiàn)圖片的慣性滑動

要實現(xiàn)圖片的慣性滑動,我們需要借助VelocityTracker來幫我們檢測當(dāng)我們手指離開圖片時的一個速度,然后根據(jù)這個速度以及圖片的位置來調(diào)用Scroller的fling方法來計算慣性滑動過程中的x和y的坐標(biāo)

 @Override
 public boolean onTouch(View v, MotionEvent event) {
 ...
  switch (event.getAction()){
   case MotionEvent.ACTION_DOWN:
    //初始化速度檢測器
    mVelocityTracker = VelocityTracker.obtain();
    if (mVelocityTracker != null){
     //將當(dāng)前的事件添加到檢測器中
     mVelocityTracker.addMovement(event);
    }
    //當(dāng)手指再次點擊到圖片時,停止圖片的慣性滑動
    if (mFlingRunnable != null){
     mFlingRunnable.cancelFling();
     mFlingRunnable = null;
    }
    ...
  }
  ...
  case MotionEvent.ACTION_MOVE:
  ...
  //如果可滑動
    if (isCanDrag){
     if (getDrawable() != null){

      if (mVelocityTracker != null){
       //將當(dāng)前事件添加到檢測器中
       mVelocityTracker.addMovement(event);
      }
      ...
    }
    ...
  case MotionEvent.ACTION_UP:
    //當(dāng)手指抬起時,將mLastPointerCount置0,停止滑動
    mLastPointerCount = 0;
    //如果當(dāng)前圖片大小小于初始化大小
    if (getScale() < mInitScale){
     //自動放大至初始化大小
     post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));
    }
    //如果當(dāng)前圖片大小大于最大值
    if (getScale() > mMaxScale){
     //自動縮小至最大值
     post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2));
    }
    if (isCanDrag){//如果當(dāng)前可以滑動
     if (mVelocityTracker != null){
      //將當(dāng)前事件添加到檢測器中
      mVelocityTracker.addMovement(event);
      //計算當(dāng)前的速度
      mVelocityTracker.computeCurrentVelocity(1000);
      //得到當(dāng)前x方向速度
      final float vX = mVelocityTracker.getXVelocity();
      //得到當(dāng)前y方向的速度
      final float vY = mVelocityTracker.getYVelocity();
      mFlingRunnable = new FlingRunnable(getContext());
      //調(diào)用fling方法,傳入控件寬高和當(dāng)前x和y軸方向的速度
      //這里得到的vX和vY和scroller需要的velocityX和velocityY的負(fù)號正好相反
      //所以傳入一個負(fù)值
      mFlingRunnable.fling(getWidth(),getHeight(),(int)-vX,(int)-vY);
      //執(zhí)行run方法
      post(mFlingRunnable);
     }
    }
    break;
 case MotionEvent.ACTION_CANCEL:
    //釋放速度檢測器
    if (mVelocityTracker != null){
     mVelocityTracker.recycle();
     mVelocityTracker = null;
    }
    break;



/**
  * 慣性滑動
  */
 private class FlingRunnable implements Runnable{
  private Scroller mScroller;
  private int mCurrentX , mCurrentY;

  public FlingRunnable(Context context){
   mScroller = new Scroller(context);
  }

  public void cancelFling(){
   mScroller.forceFinished(true);
  }

  /**
   * 這個方法主要是從onTouch中或得到當(dāng)前滑動的水平和豎直方向的速度
   * 調(diào)用scroller.fling方法,這個方法內(nèi)部能夠自動計算慣性滑動
   * 的x和y的變化率,根據(jù)這個變化率我們就可以對圖片進(jìn)行平移了
   */
  public void fling(int viewWidth , int viewHeight , int velocityX ,
       int velocityY){
   RectF rectF = getMatrixRectF();
   if (rectF == null){
    return;
   }
   //startX為當(dāng)前圖片左邊界的x坐標(biāo)
   final int startX = Math.round(-rectF.left);
   final int minX , maxX , minY , maxY;
   //如果圖片寬度大于控件寬度
   if (rectF.width() > viewWidth){
    //這是一個滑動范圍[minX,maxX],詳情見下圖
    minX = 0;
    maxX = Math.round(rectF.width() - viewWidth);
   }else{
    //如果圖片寬度小于控件寬度,則不允許滑動
    minX = maxX = startX;
   }
   //如果圖片高度大于控件高度,同理
   final int startY = Math.round(-rectF.top);
   if (rectF.height() > viewHeight){
    minY = 0;
    maxY = Math.round(rectF.height() - viewHeight);
   }else{
    minY = maxY = startY;
   }
   mCurrentX = startX;
   mCurrentY = startY;

   if (startX != maxX || startY != maxY){
    //調(diào)用fling方法,然后我們可以通過調(diào)用getCurX和getCurY來獲得當(dāng)前的x和y坐標(biāo)
    //這個坐標(biāo)的計算是模擬一個慣性滑動來計算出來的,我們根據(jù)這個x和y的變化可以模擬
    //出圖片的慣性滑動
    mScroller.fling(startX,startY,velocityX,velocityY,minX,maxX,minY,maxY);
   }

  }

關(guān)于startX,minX,maxX做一個解釋


我們從圖中可以看出,當(dāng)前圖片可滑動的一個區(qū)間就是左邊多出來的那塊區(qū)間,所以minX和maxX代表的是區(qū)間的最小值和最大值,startX就是屏幕左邊界的坐標(biāo)值,我們可以想象成是startX在區(qū)間[minX,maxX]的移動。Y軸方向同理。

現(xiàn)在我們看一下效果

以上就是本文的全部內(nèi)容,希望對大家學(xué)習(xí)Android軟件編程有所幫助。

相關(guān)文章

最新評論