2013-11-15 72 views
2

我正在實現一個可以在Stack和其他網站上找到的示例的可縮放視圖。我確實想爲視圖添加繪圖功能。我能夠找到顯示的路徑,但縮放距離它背後的圖像很遠。我使用了一個示例android圖像,沿着形狀追蹤,路徑顯示在左側和上方。另外,縮放會導致它移動(下面的圖片)。ZoomView畫布矩陣Android

我一直在玩0123.方法,其中的畫布繪製,但很少成功。我基本上使用開關是否onTouch響應移動/縮放或繪製路徑。

protected void onDraw(Canvas canvas) {    
    if(imgBitmap != null && canvas != null) 
    {        
     canvas.drawBitmap(imgBitmap, matrix, background); 
     canvas.setMatrix(matrix); 
     for (int i = 0; i < colors.size(); i++) { 
      canvas.drawPath(paths.get(i), colors.get(i)); 
     } 
     canvas.drawPath(drawPath, drawPaint); 

    } 
} 

下面是下面的代碼的圖片和其餘:

Drawing along the android causes the paths to be translated

enter image description here

public class ZoomImage extends View { 
private static final String TAG = "ZoomableImageView";  

private Bitmap imgBitmap = null; 

private int containerWidth; 
private int containerHeight; 

Paint background; 

//Matrices will be used to move and zoom image 
Matrix matrix = new Matrix(); 
Matrix savedMatrix = new Matrix(); 

PointF start = new PointF();  

float currentScale; 
float curX; 
float curY; 

//We can be in one of these 3 states 
static final int NONE = 0; 
static final int DRAG = 1; 
static final int ZOOM = 2; 
int mode = NONE; 

//For animating stuff 
float targetX; 
float targetY; 
float targetScale; 
float targetScaleX; 
float targetScaleY; 
float scaleChange; 
float targetRatio; 
float transitionalRatio; 

float easing = 0.2f; 
boolean isAnimating = false; 

float scaleDampingFactor = 0.5f; 

//For pinch and zoom 
float oldDist = 1f; 
PointF mid = new PointF(); 

private Handler mHandler = new Handler();  

float minScale; 
float maxScale = 3.0f; 

float wpRadius = 25.0f; 
float wpInnerRadius = 20.0f; 

float screenDensity; 

private GestureDetector gestureDetector; 

public static final int DEFAULT_SCALE_FIT_INSIDE = 0; 
public static final int DEFAULT_SCALE_ORIGINAL = 1; 

private int defaultScale; 


// Drawing path 
private Path drawPath; 
// Drawing and canvas paint 
public Paint drawPaint; 
// Canvas 
private Canvas drawCanvas; 
// Canvas bitmap 
private Bitmap canvasBitmap; 
// Counts how many fingers are on the screen 
int pointerCount; 
// Detects pinch to zoom activity 
// Bounds used to draw paths when zoomed in 
Rect clipBounds; 
// List of saved paths 
public ArrayList<Path> paths = new ArrayList<Path>(); 
// List of saved colors 
public ArrayList<Paint> colors = new ArrayList<Paint>(); 

// Creates Path and Paint for drawing 
public void setUpDrawing() { 
    System.out.println("--setUpDrawing--"); 
    drawPath = new Path(); 
    drawPaint = new Paint(); 

    drawPaint.setColor(Color.RED); 
    drawPaint.setAntiAlias(true); 
    drawPaint.setStrokeWidth(8); 
    drawPaint.setStyle(Paint.Style.STROKE); 
    drawPaint.setStrokeJoin(Paint.Join.ROUND); 
    drawPaint.setStrokeCap(Paint.Cap.ROUND); 
} 

public int getDefaultScale() { 
    return defaultScale; 
} 

public void setDefaultScale(int defaultScale) { 
    this.defaultScale = defaultScale; 
} 

public ZoomImage(Context context) { 
    super(context);  
    setFocusable(true); 
    setFocusableInTouchMode(true); 

    screenDensity = context.getResources().getDisplayMetrics().density; 

    initPaints(); 
    gestureDetector = new GestureDetector(new MyGestureDetector());  
    setUpDrawing(); 
} 

public ZoomImage(Context context, AttributeSet attrs) { 
    super(context, attrs); 

    screenDensity = context.getResources().getDisplayMetrics().density;  
    initPaints(); 
    gestureDetector = new GestureDetector(new MyGestureDetector()); 

    defaultScale = ZoomImage.DEFAULT_SCALE_FIT_INSIDE; 
    setUpDrawing(); 
} 

private void initPaints() { 
    background = new Paint(); 
    setUpDrawing(); 
} 

@Override 
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { 
    super.onSizeChanged(width, height, oldWidth, oldHeight); 

    //Reset the width and height. Will draw bitmap and change 
    containerWidth = width; 
    containerHeight = height; 

    if(imgBitmap != null) { 
     drawCanvas = new Canvas(imgBitmap); 
     int imgHeight = imgBitmap.getHeight(); 
     int imgWidth = imgBitmap.getWidth(); 

     float scale; 
     int initX = 0; 
     int initY = 0;   

     if(defaultScale == ZoomImage.DEFAULT_SCALE_FIT_INSIDE) {    
      if(imgWidth > containerWidth) {   
       scale = (float)containerWidth/imgWidth;   
       float newHeight = imgHeight * scale;   
       initY = (containerHeight - (int)newHeight)/2; 

       matrix.setScale(scale, scale); 
       matrix.postTranslate(0, initY); 
      } 
      else {   
       scale = (float)containerHeight/imgHeight; 
       float newWidth = imgWidth * scale; 
       initX = (containerWidth - (int)newWidth)/2; 

       matrix.setScale(scale, scale); 
       matrix.postTranslate(initX, 0); 
      } 

      curX = initX; 
      curY = initY; 

      currentScale = scale; 
      minScale = scale; 
     } 
     else { 
      if(imgWidth > containerWidth) {         
       initY = (containerHeight - (int)imgHeight)/2;     
       matrix.postTranslate(0, initY); 
      } 
      else {        
       initX = (containerWidth - (int)imgWidth)/2;     
       matrix.postTranslate(initX, 0); 
      } 

      curX = initX; 
      curY = initY; 

      currentScale = 1.0f; 
      minScale = 1.0f;    
     } 


     invalidate();   
    } 
} 

@Override 
protected void onDraw(Canvas canvas) {    
    if(imgBitmap != null && canvas != null) 
    {        
     canvas.drawBitmap(imgBitmap, matrix, background); 
     canvas.setMatrix(matrix); 
     for (int i = 0; i < colors.size(); i++) { 
      canvas.drawPath(paths.get(i), colors.get(i)); 
     } 
     canvas.drawPath(drawPath, drawPaint); 

    } 
} 

public void reDrawUndo() { 
    System.out.println("paths.size" + paths.size()); 
    if (paths.size() > 0) { 
     paths.remove(paths.size() - 1); 
     colors.remove(colors.size() - 1); 
     invalidate(); 
    } 
} 

//Checks and sets the target image x and y co-ordinates if out of bounds 
private void checkImageConstraints() { 
    if(imgBitmap == null) { 
     return; 
    } 

    float[] mvals = new float[9]; 
    matrix.getValues(mvals); 

    currentScale = mvals[0]; 

    if(currentScale < minScale) {        
     float deltaScale = minScale/currentScale;     
     float px = containerWidth/2; 
     float py = containerHeight/2;   
     matrix.postScale(deltaScale, deltaScale, px, py); 
     invalidate(); 
    }  

    matrix.getValues(mvals); 
    currentScale = mvals[0]; 
    curX = mvals[2]; 
    curY = mvals[5]; 

    int rangeLimitX = containerWidth - (int)(imgBitmap.getWidth() * currentScale); 
    int rangeLimitY = containerHeight - (int)(imgBitmap.getHeight() * currentScale); 


    boolean toMoveX = false; 
    boolean toMoveY = false; 

    if(rangeLimitX < 0) { 
     if(curX > 0) { 
      targetX = 0; 
      toMoveX = true; 
     } 
     else if(curX < rangeLimitX) { 
      targetX = rangeLimitX; 
      toMoveX = true; 
     } 
    } 
    else { 
     targetX = rangeLimitX/2; 
     toMoveX = true; 
    } 

    if(rangeLimitY < 0) { 
     if(curY > 0) { 
      targetY = 0; 
      toMoveY = true; 
     } 
     else if(curY < rangeLimitY) { 
      targetY = rangeLimitY; 
      toMoveY = true; 
     } 
    } 
    else { 
     targetY = rangeLimitY/2; 
     toMoveY = true; 
    } 

    if(toMoveX == true || toMoveY == true) { 
     if(toMoveY == false) { 
      targetY = curY; 
     } 
     if(toMoveX == false) { 
      targetX = curX; 
     }   

     //Disable touch event actions 
     isAnimating = true; 
     //Initialize timer   
     mHandler.removeCallbacks(mUpdateImagePositionTask); 
     mHandler.postDelayed(mUpdateImagePositionTask, 100); 
    } 
}  


@Override 
public boolean onTouchEvent(MotionEvent event) { 
    float touchX = event.getX(); 
    System.out.println("touchX: " + event.getX()); 
    float touchY = event.getY(); 
    System.out.println("touchY: " + event.getY()); 

    // Is drawing mode on? 
    if (Deal.on){ 
     switch (event.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
       drawPath.moveTo(touchX, touchY); 
       break; 
      case MotionEvent.ACTION_MOVE: 
       drawPath.lineTo(touchX, touchY); 
       break; 
      case MotionEvent.ACTION_UP: 
       Paint newPaint = new Paint(); 
       newPaint.set(drawPaint); 
       colors.add(newPaint); 
       paths.add(drawPath); 
       drawPath = new Path(); 
       drawPath.reset(); 
       break; 
      case MotionEvent.ACTION_POINTER_DOWN: 
       System.out.println("bleh"); 
       break; 
      default: 
       return false; 
     } 
    } 
    else { 
     if(gestureDetector.onTouchEvent(event)) { 
      return true; 
     } 

     if(isAnimating == true) { 
      return true; 
     } 

     //Handle touch events here  
     float[] mvals = new float[9]; 
     switch(event.getAction() & MotionEvent.ACTION_MASK) { 
     case MotionEvent.ACTION_DOWN: 
      if(isAnimating == false) { 
       savedMatrix.set(matrix); 
       start.set(event.getX(), event.getY());   
       mode = DRAG;    
      } 
     break; 

     case MotionEvent.ACTION_POINTER_DOWN: 
      oldDist = spacing(event);   
      if(oldDist > 10f) { 
       savedMatrix.set(matrix); 
       midPoint(mid, event); 
       mode = ZOOM; 
      } 
     break; 

     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_POINTER_UP: 
      mode = NONE; 

      matrix.getValues(mvals); 
      curX = mvals[2]; 
      curY = mvals[5]; 
      currentScale = mvals[0]; 

      if(isAnimating == false) {          
       checkImageConstraints(); 
      } 
     break; 

     case MotionEvent.ACTION_MOVE:   
      if(mode == DRAG && isAnimating == false) { 
       matrix.set(savedMatrix); 
       float diffX = event.getX() - start.x; 
       float diffY = event.getY() - start.y; 

       matrix.postTranslate(diffX, diffY); 

       matrix.getValues(mvals); 
       curX = mvals[2]; 
       curY = mvals[5]; 
       currentScale = mvals[0]; 
      } 
      else if(mode == ZOOM && isAnimating == false) { 
       float newDist = spacing(event);    
       if(newDist > 10f) { 
        matrix.set(savedMatrix); 
        float scale = newDist/oldDist;     
        matrix.getValues(mvals); 
        currentScale = mvals[0]; 

        if(currentScale * scale <= minScale) { 
         matrix.postScale(minScale/currentScale, minScale/currentScale, mid.x, mid.y); 
        }     
        else if(currentScale * scale >= maxScale) { 
         matrix.postScale(maxScale/currentScale, maxScale/currentScale, mid.x, mid.y); 
        } 
        else { 
         matrix.postScale(scale, scale, mid.x, mid.y); 
        } 


        matrix.getValues(mvals); 
        curX = mvals[2]; 
        curY = mvals[5]; 
        currentScale = mvals[0];          
       } 
      } 

    break;        
    } 
    } 
    //Calculate the transformations and then invalidate 
    invalidate(); 
    return true; 
} 

private float spacing(MotionEvent event) { 
    float x = event.getX(0) - event.getX(1); 
    float y = event.getY(0) - event.getY(1); 
    return FloatMath.sqrt(x * x + y * y); 
} 

private void midPoint(PointF point, MotionEvent event) { 
    float x = event.getX(0) + event.getX(1); 
    float y = event.getY(0) + event.getY(1); 
    point.set(x/2, y/2); 
} 

public void setImageBitmap(Bitmap b) {  
    if(b != null) { 
     imgBitmap = b;    

     containerWidth = getWidth(); 
     containerHeight = getHeight(); 

     int imgHeight = imgBitmap.getHeight(); 
     int imgWidth = imgBitmap.getWidth(); 

     float scale; 
     int initX = 0; 
     int initY = 0; 

     matrix.reset(); 

     if(defaultScale == ZoomImage.DEFAULT_SCALE_FIT_INSIDE) {    
      if(imgWidth > containerWidth) {   
       scale = (float)containerWidth/imgWidth;   
       float newHeight = imgHeight * scale;   
       initY = (containerHeight - (int)newHeight)/2; 

       matrix.setScale(scale, scale); 
       matrix.postTranslate(0, initY); 
      } 
      else {   
       scale = (float)containerHeight/imgHeight; 
       float newWidth = imgWidth * scale; 
       initX = (containerWidth - (int)newWidth)/2; 

       matrix.setScale(scale, scale); 
       matrix.postTranslate(initX, 0); 
      } 

      curX = initX; 
      curY = initY; 

      currentScale = scale; 
      minScale = scale; 
     } 
     else { 
      if(imgWidth > containerWidth) { 
       initX = 0; 
       if(imgHeight > containerHeight) {      
        initY = 0; 
       } 
       else {      
        initY = (containerHeight - (int)imgHeight)/2; 
       } 

       matrix.postTranslate(0, initY); 
      } 
      else {        
       initX = (containerWidth - (int)imgWidth)/2; 
       if(imgHeight > containerHeight) { 
        initY = 0; 
       } 
       else { 
        initY = (containerHeight - (int)imgHeight)/2; 
       } 
       matrix.postTranslate(initX, 0); 
      } 

      curX = initX; 
      curY = initY; 

      currentScale = 1.0f; 
      minScale = 1.0f;    
     } 

     invalidate();   
    } 
    else { 
     Log.d(TAG, "bitmap is null"); 
    } 
} 

public Bitmap getPhotoBitmap() {  
    return imgBitmap; 
} 


private Runnable mUpdateImagePositionTask = new Runnable() { 
    public void run() {  
     float[] mvals; 

     if(Math.abs(targetX - curX) < 5 && Math.abs(targetY - curY) < 5) { 
      isAnimating = false; 
      mHandler.removeCallbacks(mUpdateImagePositionTask); 

      mvals = new float[9]; 
      matrix.getValues(mvals); 

      currentScale = mvals[0]; 
      curX = mvals[2]; 
      curY = mvals[5]; 

      //Set the image parameters and invalidate display 
      float diffX = (targetX - curX); 
      float diffY = (targetY - curY); 

      matrix.postTranslate(diffX, diffY); 
     } 
     else { 
      isAnimating = true; 
      mvals = new float[9]; 
      matrix.getValues(mvals); 

      currentScale = mvals[0]; 
      curX = mvals[2]; 
      curY = mvals[5]; 

      //Set the image parameters and invalidate display 
      float diffX = (targetX - curX) * 0.3f; 
      float diffY = (targetY - curY) * 0.3f; 

      matrix.postTranslate(diffX, diffY);    
      mHandler.postDelayed(this, 25);    
     } 

     invalidate();   
    } 
}; 

private Runnable mUpdateImageScale = new Runnable() { 
    public void run() {   
     float transitionalRatio = targetScale/currentScale;   
     float dx; 
     if(Math.abs(transitionalRatio - 1) > 0.05) { 
      isAnimating = true;    
      if(targetScale > currentScale) {          
       dx = transitionalRatio - 1; 
       scaleChange = 1 + dx * 0.2f; 

       currentScale *= scaleChange; 

       if(currentScale > targetScale) { 
        currentScale = currentScale/scaleChange; 
        scaleChange = 1; 
       } 
      } 
      else {         
       dx = 1 - transitionalRatio;     
       scaleChange = 1 - dx * 0.5f; 
       currentScale *= scaleChange; 

       if(currentScale < targetScale) { 
        currentScale = currentScale/scaleChange; 
        scaleChange = 1; 
       } 
      } 


      if(scaleChange != 1) { 
       matrix.postScale(scaleChange, scaleChange, targetScaleX, targetScaleY);    
       mHandler.postDelayed(mUpdateImageScale, 15); 
       invalidate(); 
      } 
      else { 
       isAnimating = false; 
       scaleChange = 1;     
       matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY); 
       currentScale = targetScale; 
       mHandler.removeCallbacks(mUpdateImageScale); 
       invalidate(); 
       checkImageConstraints(); 
      }    
     } 
     else { 
      isAnimating = false; 
      scaleChange = 1;    
      matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY); 
      currentScale = targetScale; 
      mHandler.removeCallbacks(mUpdateImageScale); 
      invalidate(); 
      checkImageConstraints(); 
     }        
    } 
}; 

class MyGestureDetector extends SimpleOnGestureListener { 
    @Override 
    public boolean onDoubleTap(MotionEvent event) {   
     if(isAnimating == true) { 
      return true; 
     } 

     scaleChange = 1; 
     isAnimating = true; 
     targetScaleX = event.getX(); 
     targetScaleY = event.getY(); 

     if(Math.abs(currentScale - maxScale) > 0.1) {   
      targetScale = maxScale; 
     } 
     else { 
      targetScale = minScale; 
     } 
     targetRatio = targetScale/currentScale; 
     mHandler.removeCallbacks(mUpdateImageScale); 
     mHandler.post(mUpdateImageScale);   
     return true; 
    } 

    @Override 
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 
     return super.onFling(e1, e2, velocityX, velocityY); 
    } 

    @Override 
    public boolean onDown(MotionEvent e) { 
     return false; 
    } 
} 

}

編輯: 添加canvas.concat(matrix)固定縮放。仍然試圖解決偏移...

的onDraw改爲:

super.onDraw(canvas); 
    canvas.save(); 
    if(imgBitmap != null && canvas != null){  
     if(drawCanvas == null) 
      drawCanvas = new Canvas(imgBitmap); 

     clipBounds = canvas.getClipBounds(); 
     canvas.drawBitmap(imgBitmap, matrix, background); 
     canvas.concat(matrix); 
     for (int i = 0; i < colors.size(); i++) { 
      canvas.drawPath(paths.get(i), colors.get(i)); 
     } 
     canvas.drawPath(drawPath, drawPaint); 

    } 
    canvas.restore(); 
    invalidate(); 
+0

嗨,我面臨同樣的問題。我無法確定canvas.concat需要包含在哪裏。提前致謝。 – siva

+0

canvas.concat進入onDraw方法 – ono

回答

3

明白了!獲取比例和偏移的矩陣值,並相應地調整touchX和touchY。 mv [4]是尺度,而mv [2]和mv [5]分別是x和y的偏移量。

float[] mv = new float[9]; 

@Override 
public boolean onTouchEvent(MotionEvent event) { 

    // Get the values from the matrix into the float array 
    matrix.getValues(mv); 

    float touchX = (event.getX()*(1/mv[4]) - (mv[2]/mv[4])); 
    float touchY = (event.getY()*(1/mv[4]) - (mv[5]/mv[4])); 
+0

Interstingly,我有同樣的問題。所以遵循你的代碼。但是當我用縮放狀態下的手指書寫時,角色會被翻譯一段距離? – siva

+0

什麼字符?繪圖與你的手指不在同一條路徑上? – ono

+0

我的意思是由手指所產生的路徑。你可以請發佈該視圖班級嗎? – siva