2012-07-05 71 views
7

我一直在這個問題上停留了八個小時,所以我覺得是時候得到一些幫助。畫布捏縮放到點範圍內

在開始我的問題之前,我只會讓我們知道我已經瀏覽過此網站和Google,而且我找到的答案都沒有幫助。 (This是一個,anotheranother

這裏的交易:我有一個擴展SurfaceView類(姑且稱之爲MySurface),並覆蓋在它的許多方法。通常,它會繪製幾個正方形和文本框,這很好。只要用戶開始觸摸,它就會轉換爲Bitmap,然後繪製直到用戶釋放的每個幀。

這裏是蹭:我想實現這樣的功能,用戶可以將兩個手指放在屏幕上,捏縮放,也可以平移(但只有兩個手指向下)。

我發現捏到變焦的幾個實現,並通過以下他們適應我Canvas對象MySurface

public void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    canvas.save(); 

    canvas.scale(mScaleVector.z, mScaleVector.z); // This is the scale factor as seen below 
    canvas.translate(mScaleVector.x, mScaleVector.y); // These are offset values from 0,0, both working fine 

    // Start draw code 

    // ... 

    // End draw code 

    canvas.restore(); 
} 
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
    @Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     float factor = detector.getScaleFactor(); 
     if (Math.abs(factor - 1.0f) >= 0.0075f) { 
      mScaleVector.z *= factor; 
      mScaleVector.z = Math.max(MIN_ZOOM, Math.min(mScaleVector.z, MAX_ZOOM)); 
     } 

     // ... 

     invalidate(); 

     return true; 
    } 
} 
public boolean onTouchEvent(MotionEvent event) { 
    int action = event.getAction() & MotionEvent.ACTION_MASK; 
    int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
    if (event.getPointerCount() == 2) { 
     if (action == MotionEvent.ACTION_POINTER_DOWN && pointerIndex == 1) { 
      // The various pivot coordinate codes would belong here 
     } 
    } 

    detector.onTouchEvent(event); // Calls the Scale Gesture Detector 
    return true; 
} 

雖然這兩個元素做工精細 - 來回捏滾動 - 縮放 - 有一個大問題。縮放到縮放時,放大到點0,0,而不是放大到手指點。

我已經嘗試了很多辦法來解決這個問題:

  • 使用canvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y);;顯然,這會產生不想要的結果,因爲mScaleVector x和y值是0偏移量。
  • 管理使用與translate()方法相同的偏移量的「數據透視」座標,但這會產生相同的0,0問題,或者在觸摸視圖時跳轉。
  • 許多其他的事情......我已經做了很多與上述樞軸座標,試圖將其位置基於用戶的第一次觸摸,並將其相對於觸摸每個連續的手勢。

此外,此畫布必須是有界的,所以用戶不能永久滾動。但是,當我使用.scale(sx, sy, px, py)方法時,它會將事物推到我在.translate()中設置的任何邊界之外。

我很...在這一點上很開放。我知道可以添加此功能,因爲它可以在Android 4.0圖庫中查看(查看單個圖像時)。我試圖追蹤處理這個問題的源代碼,但無濟於事。

+0

我個人直接使用'onTouchEvent'處理兩個手指位置。有了這個,你可以很容易地找到中點,以及需要多大的縮放比例。 – Doomsknight

+0

如果你已經解決了我面臨同樣問題的問題,你可以請發佈解決方案,我的客戶是...... :(或者你能告訴我你是如何解決問題的嗎?謝謝 – AkashG

回答

13

這裏是我用來實現使用ScaleGestureDetectorImageView縮放縮放的代碼。只需很少修改或者不需要修改,您也應該可以使用它,因爲您也可以使用轉換關係來繪製Canvas

@Override 
public boolean onScale(ScaleGestureDetector detector) { 
    float mScaleFactor = (float) Math.min(
     Math.max(.8f, detector.getScaleFactor()), 1.2); 
    float origScale = saveScale; 
    saveScale *= mScaleFactor; 
    if (saveScale > maxScale) { 
     saveScale = maxScale; 
     mScaleFactor = maxScale/origScale; 
    } else if (saveScale < minScale) { 
     saveScale = minScale; 
     mScaleFactor = minScale/origScale; 
    } 
    right = width * saveScale - width 
      - (2 * redundantXSpace * saveScale); 
    bottom = height * saveScale - height 
      - (2 * redundantYSpace * saveScale); 
    if (origWidth * saveScale <= width 
      || origHeight * saveScale <= height) { 
     matrix.postScale(mScaleFactor, mScaleFactor, width/2, height/2); 
     if (mScaleFactor < 1) { 
      matrix.getValues(m); 
      float x = m[Matrix.MTRANS_X]; 
      float y = m[Matrix.MTRANS_Y]; 
      if (mScaleFactor < 1) { 
       if (Math.round(origWidth * saveScale) < width) { 
        if (y < -bottom) 
         matrix.postTranslate(0, -(y + bottom)); 
        else if (y > 0) 
         matrix.postTranslate(0, -y); 
       } else { 
        if (x < -right) 
         matrix.postTranslate(-(x + right), 0); 
        else if (x > 0) 
         matrix.postTranslate(-x, 0); 
       } 
      } 
     } 
    } else { 
     matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY()); 
     matrix.getValues(m); 
     float x = m[Matrix.MTRANS_X]; 
     float y = m[Matrix.MTRANS_Y]; 
     if (mScaleFactor < 1) { 
      if (x < -right) 
       matrix.postTranslate(-(x + right), 0); 
      else if (x > 0) 
       matrix.postTranslate(-x, 0); 
      if (y < -bottom) 
       matrix.postTranslate(0, -(y + bottom)); 
      else if (y > 0) 
       matrix.postTranslate(0, -y); 
     } 
    } 
    return true; 
} 

就我而言,我計算在視圖的onMeasure()方法neccesary值,你可能想要做你的SurfaceView

width = MeasureSpec.getSize(widthMeasureSpec); // Change this according to your screen size 
height = MeasureSpec.getSize(heightMeasureSpec); // Change this according to your screen size 

// Fit to screen. 
float scale; 
float scaleX = (float) width/(float) bmWidth; 
float scaleY = (float) height/(float) bmHeight; 
scale = Math.min(scaleX, scaleY); 
matrix.setScale(scale, scale); 
setImageMatrix(matrix); 
saveScale = 1f; 
scaleMappingRatio = saveScale/scale; 

// Center the image 
redundantYSpace = (float) height - (scale * (float) bmHeight); 
redundantXSpace = (float) width - (scale * (float) bmWidth); 
redundantYSpace /= (float) 2; 
redundantXSpace /= (float) 2; 

matrix.postTranslate(redundantXSpace, redundantYSpace); 

origWidth = width - 2 * redundantXSpace; 
origHeight = height - 2 * redundantYSpace; 
right = width * saveScale - width - (2 * redundantXSpace * saveScale); 
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale); 
setImageMatrix(matrix); 

一點解釋這個其他地方:

saveScale是當前的比例Bitmap

mScaleFactor is th電子因素,你必須乘以你的比例。

maxScaleminScale可以是恆定值。

widthheight是屏幕的尺寸。

redundantXSpaceredundantYSpace是圖像邊界和屏幕邊緣之間空,因爲當在它更小,則屏幕

origHeightorigWidth是位圖的大小

matrix是對圖像的中心用於繪製位圖的當前轉換矩陣

訣竅是,當我初始化縮放並居中初始化圖像時,我將該縮放比例選爲1,並將scaleMappingRatio我映射了相對於該圖像的實際比例值。

+2

哦,並且徹底地測試你的代碼的各個方面,我發現那些矩陣變換是關鍵!如果可以的話,我會給你買一杯啤酒!A +,好的先生。 – Eric

+1

不客氣,我花了很多時間找出正確的方法,當我開始編寫上面的代碼時,我認爲它會值得分享。 –

+0

@AdamL.Mónos偉大的工作亞當,歡呼! –

-1
protected override void OnDraw(Canvas canvas) 
    { 
     canvas.Save(); 

     int x = (int)(canvas.Width * mScaleFactor - canvas.Width)/2; 
     int y = (int)(canvas.Height * mScaleFactor - canvas.Height)/2; 

     if (mPosX > (canvas.Width + x - 50)) 
     { 
      mPosX = canvas.Width + x - 50; 
     } 
     if (mPosX < (50 - canvas.Width - x)) 
     { 
      mPosX = 50 - canvas.Width - x; 
     } 
     if (mPosY > (canvas.Height + y - 50)) 
     { 
      mPosY = canvas.Height + y - 50; 
     } 
     if (mPosY < (50 - canvas.Height - y)) 
     { 
      mPosY = 50 - canvas.Height - y; 
     } 

     canvas.Translate(mPosX, mPosY); 
     canvas.Scale(mScaleFactor, mScaleFactor, canvas.Width/2, canvas.Height/2); 
     base.OnDraw(canvas); 
     canvas.Restore(); 
    } 

這是monodroid代碼,但可以很容易地轉換爲JAVA。 你可以爲你的邊界選擇任何值(這裏是50像素邊距)

+0

這裏是一個庫,將這樣做 https://stackoverflow.com/questions/6578320 /如何對申請變焦拖和旋轉到的圖像功能於機器人/ 47861501#47861501 – user2288580