2013-10-18 110 views
11

我目前在webview圖像(下圖中的大象)上實現繪圖功能。我沒有繪製它的問題,但縮放功能在繪圖上做了一些時髦的東西(下面的第二張圖片)。當我放大時,繪圖不會縮放,而是移位。在縮放時繪製也不起作用。我的代碼如下:畫布 - 在Android上放大,移動和縮放

Normal

enter image description here

@Override 
protected void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 
    canvas.save(); 
    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint); 
    canvas.drawPath(drawPath, drawPaint); 
    canvas.scale(mScaleFactor, mScaleFactor); 
    canvas.restore(); 
    clipBounds = canvas.getClipBounds(); 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    mScaleDetector.onTouchEvent(event); 
    float touchX = event.getX()/mScaleFactor + clipBounds.left; 
    float touchY = event.getY()/mScaleFactor + clipBounds.top; 
    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: 
       drawCanvas.drawPath(drawPath, drawPaint); 
       drawPath.reset(); 
       break; 
      default: 
       return false; 
    } 
    else { 
     super.onTouchEvent(event); 
    } 
    invalidate(); 
    return true; 
} 

public class ScaleGestureListener extends SimpleOnScaleGestureListener { 
    @Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     mScaleFactor *= detector.getScaleFactor(); 

     // Don't let the object get too small or too large. 
     mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); 

     invalidate(); 

     return true; 
    } 
} 

編輯:我設法讓畫布使用GestureDetector的比例因子縮放重繪。另外,我有一個從繪圖切換到縮放/ webview控件的開關。我遇到的一個問題是,雙擊WebView不會觸發onScale手勢。這意味着畫布不會重新繪製並移動放大。

我需要實現一個功能,該功能可以檢測縮放倍數受雙擊放大影響的程度。如果任何人都可以提出解決方案。以下是更新代碼:

@Override 
protected void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 
    clipBounds = canvas.getClipBounds(); 
    canvas.save(); 
    drawPaint.setStrokeWidth(8/mScaleFactor); 
    canvas.scale(mScaleFactor, mScaleFactor, 0, 0); 
    canvas.drawPath(drawPath, drawPaint); 
    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint); 
    canvas.restore(); 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    pointerCount = event.getPointerCount(); 
    mScaleDetector.onTouchEvent(event); 
    float touchX = (event.getX() + clipBounds.left)/mScaleFactor; 
    float touchY = (event.getY() + clipBounds.top)/mScaleFactor; 
    if (Deal.on){ 
     if (event.getPointerCount() > 1){ 
      return false; 
     } 
     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: 
       drawCanvas.drawPath(drawPath, drawPaint); 
       drawPath.reset(); 
       break; 
      default: 
       return false; 
     } 
    } 
    else { 
     super.onTouchEvent(event); 
    } 
    invalidate(); 
    return true; 
} 

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    // Try for a width based on our minimum 
    int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth(); 
    int w = resolveSizeAndState(minw, widthMeasureSpec, 1); 

    //int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop(); 
    int h = resolveSizeAndState(MeasureSpec.getSize(w), heightMeasureSpec, 0); 
    setMeasuredDimension(w, h); 
} 

public class ScaleGestureListener extends SimpleOnScaleGestureListener { 

    @Override 
    public boolean onScale(ScaleGestureDetector detector) { 

     // Don't let the object get too small or too large. 
     if(Deal.on == false) { 
      mScaleFactor *= detector.getScaleFactor(); 
      mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 20.0f)); 
     } 

     invalidate(); 

     return true; 
    } 
} 
+2

你嘗試,你做任何與畫布繪製前進行的規模縮放的責任? –

+1

問題是?爲什麼繪圖沒有縮放或爲什麼它不起作用?很顯然,你每次都會用同樣的顏料進行繪製,爲什麼它應該放大? – sandrstar

+0

爲什麼你在doubleTap上放大縮小,以及在哪裏實現?如果需要,您必須相應地更改scaleFactor。 –

回答

2

我已經實現了這種行爲,但方式稍有不同。我用矩陣來處理所有的縮放和滾動(在我的情況下是旋轉)。它使整齊的代碼和發條工作。不過,我不知道是什麼導致了你的時髦行爲。

存儲矩陣,另一路爲類成員:

Matrix drawMatrix = new Matrix(); 
Path transformedPath = new Path(); 

替換您onScale:

@Override 
public boolean onScale(ScaleGestureDetector detector) { 
    Matrix transformationMatrix = new Matrix(); 

    //Zoom focus is where the fingers are centered, 
    transformationMatrix.postTranslate(-detector.getFocusX(), -detector.getFocusY()); 

    transformationMatrix.postScale(detector.getScaleFactor(), detector.getScaleFactor()); 

/* Using getFocuShift allows for scrolling with two pointers down. Remove it to skip this functionality */ 
    transformationMatrix.postTranslate(detector.getFocusX() + detector.getFocusShiftX(), detector.getFocusY() + detector.getFocusShiftY()); 

    drawMatrix.postConcat(transformationMatrix); 
    invalidate(); 
    return true; 
} 
中的onDraw

;跳過保存畫布,而不是:

@Override 
protected void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    canvas.drawBitmap(canvasBitmap, drawMatrix, canvasPaint); 
    transformedPath.rewind(); 
    transformedMatrix.addPath(drawPath); 
    transformedPath.transform(drawMatrix, null); 
    canvas.drawPath(transformedPath, drawPaint); 
} 

快樂編碼!

+0

實際上,這實際上不會縮放路徑。它將繪製放大位置,但線寬將與原來的相同。當我這樣做時,這就是我想要的,但也許不是你想要的。 –

+0

線寬應與縮放比例;如果放大,它應該變得更厚。 – ono

+0

檢查我的編輯。我幾乎得到了它的工作方式 – ono

0

完成所有你想在一個非常簡單的方式做的是利用帆布方法

canvas.drawBitmap(位圖位圖,矩形SRC,DST矩形,油漆的不同效果的一個好方法塗料);

這種方法允許你把整個源位圖或它只是一個小樣本(矩形SRC),並投如你所願(矩形DST)自動執行矩陣計算的縮放/翻譯爲您如示於下面的例子:

該實施例只是縮放/縮小整個圖像...

 Canvas canvas = null; 
    Bitmap elephantBmp = null; 
    Rect src = new Rect(); 
    Rect postRect = new Rect(); 
    Paint paintIfAny = new Paint(); 

    //To scale the whole image, first take the whole source and then postRect with a bigger size... 
    src.top = src.left = 0; 
    src.right = elephantBmp.getWidth(); 
    src.bottom = elephantBmp.getHeight(); 
    //Here you have the chance to translate/scale the image(in this case i will not translate it but just scale it...) 
    postRect.top = postRect.left = 0; 
    postRect.right = (int)(elephantBmp.getWidth() * 1.1F); 
    postRect.bottom = (int)(elephantBmp.getHeight() * 1.1F); 

    canvas.drawBitmap(elephantBmp, src, postRect, paintIfAny); 

而本實施例中只需要一個圖像的樣本,並將其顯示「內變焦效果」

 Canvas canvas = null; 
    Bitmap elephantBmp = null; 
    Rect src = new Rect(); 
    Rect postRect = new Rect(); 
    Paint paintIfAny = new Paint(); 

    //To shift the whole image, first take the part of the original source image you want to show 
    src.top = topPosition;//Top coordinate of piece of image to show 
    src.left = leftPosition;//Left coordinate of piece of image to show 
    src.right = rightPosition;//Right coordinate of piece of image to show 
    src.bottom = bottomPosition;//Bottom coordinate of piece of image to show 
    //Here you have the chance to show it as big as you want... 
    postRect.top = postRect.left = 0; 
    postRect.right = (int)(src.width() * 1.1F); 
    postRect.bottom = (int)(src.height() * 1.1F); 

    canvas.drawBitmap(elephantBmp, src, postRect, paintIfAny); 

通過利用這些SRC/DST的對象,你可以做很多你想在Android任何影響的,是一種簡單,真正強大的工具

希望這有助於。

問候!

+0

檢查我的編輯。我幾乎是按照自己的方式運作的 – ono

0

你可以嘗試繪製路徑只有一次,並保持在畫布本身

private boolean pathsDrawn; 

protected void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 
    clipBounds = canvas.getClipBounds(); 
    canvas.save(); 
    drawPaint.setStrokeWidth(8/mScaleFactor); 
    canvas.scale(mScaleFactor, mScaleFactor, 0, 0); 

    if(!pathsDrawn) { 
     canvas.drawPath(drawPath, drawPaint); 
     pathsDrawn = true; 
    } 
    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint); 
    canvas.restore(); 
} 
+0

當我將手指放開時,這只是繪製了路徑。它不影響縮放 – ono

0
check below code will help you. 

//CUSTOM IMAGEVIEW 
import java.io.ByteArrayOutputStream; 
import android.annotation.SuppressLint; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.graphics.RectF; 
import android.graphics.drawable.Drawable; 
import android.util.AttributeSet; 
import android.util.FloatMath; 
import android.view.GestureDetector; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 
import android.widget.ImageView; 

public class ScaleImageView extends ImageView implements OnTouchListener { 

static final float STROKE_WIDTH = 10f; 
static final float HALF_STROKE_WIDTH = STROKE_WIDTH/2; 

float lastTouchX; 
float lastTouchY; 
final RectF dirtyRect = new RectF(); 

private Context mContext; 
private float MAX_SCALE = 2f; 

private static Matrix mMatrix; 
private final float[] mMatrixValues = new float[9]; 

// display width height. 
private int mWidth; 
private int mHeight; 

private int mIntrinsicWidth; 
private int mIntrinsicHeight; 

private float mScale; 
private float mMinScale; 

private float mPrevDistance; 
private boolean isScaling; 

private int mPrevMoveX; 
private int mPrevMoveY; 
private GestureDetector mDetector; 

Paint paint = new Paint(); 
public static Path path = new Path(); 

public static int imageheight, imagewidth; 

String TAG = "ScaleImageView"; 

public ScaleImageView(Context context, AttributeSet attr) { 
super(context, attr); 
this.mContext = context; 
initialize(); 
} 

public ScaleImageView(Context context) { 
super(context); 
this.mContext = context; 
initialize(); 
} 

private void resetDirtyRect(float eventX, float eventY) { 
dirtyRect.left = Math.min(lastTouchX, eventX); 
dirtyRect.right = Math.max(lastTouchX, eventX); 
dirtyRect.top = Math.min(lastTouchY, eventY); 
dirtyRect.bottom = Math.max(lastTouchY, eventY); 
} 

@Override 
public void setImageBitmap(Bitmap bm) { 
super.setImageBitmap(bm); 
this.initialize(); 
} 

@Override 
public void setImageResource(int resId) { 
super.setImageResource(resId); 
this.initialize(); 
} 

private void initialize() { 
this.setScaleType(ScaleType.MATRIX); 
this.mMatrix = new Matrix(); 
Drawable d = getDrawable(); 

paint.setAntiAlias(true); 
paint.setColor(Color.RED); 
paint.setStyle(Paint.Style.STROKE); 
paint.setStrokeJoin(Paint.Join.ROUND); 
paint.setStrokeWidth(STROKE_WIDTH); 

if (d != null) { 
    mIntrinsicWidth = d.getIntrinsicWidth(); 
    mIntrinsicHeight = d.getIntrinsicHeight(); 
    setOnTouchListener(this); 
} 
mDetector = new GestureDetector(mContext, 
     new GestureDetector.SimpleOnGestureListener() { 
      @Override 
      public boolean onDoubleTap(MotionEvent e) { 
       maxZoomTo((int) e.getX(), (int) e.getY()); 
       cutting(); 
       return super.onDoubleTap(e); 
      } 
     }); 

} 

@Override 
protected boolean setFrame(int l, int t, int r, int b) { 
mWidth = r - l; 
mHeight = b - t; 

mMatrix.reset(); 
int r_norm = r - l; 
mScale = (float) r_norm/(float) mIntrinsicWidth; 

int paddingHeight = 0; 
int paddingWidth = 0; 
// scaling vertical 
if (mScale * mIntrinsicHeight > mHeight) { 
    mScale = (float) mHeight/(float) mIntrinsicHeight; 
    mMatrix.postScale(mScale, mScale); 
    paddingWidth = (r - mWidth)/2; 
    paddingHeight = 0; 
    // scaling horizontal 
} else { 
    mMatrix.postScale(mScale, mScale); 
    paddingHeight = (b - mHeight)/2; 
    paddingWidth = 0; 
} 
mMatrix.postTranslate(paddingWidth, paddingHeight); 

setImageMatrix(mMatrix); 
mMinScale = mScale; 
zoomTo(mScale, mWidth/2, mHeight/2); 
cutting(); 
return super.setFrame(l, t, r, b); 
} 

protected float getValue(Matrix matrix, int whichValue) { 
matrix.getValues(mMatrixValues); 
return mMatrixValues[whichValue]; 
} 

protected float getScale() { 
return getValue(mMatrix, Matrix.MSCALE_X); 
} 

public float getTranslateX() { 
return getValue(mMatrix, Matrix.MTRANS_X); 
} 

protected float getTranslateY() { 
return getValue(mMatrix, Matrix.MTRANS_Y); 
} 

protected void maxZoomTo(int x, int y) { 
if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) { 
    // threshold 0.1f 
    float scale = mMinScale/getScale(); 
    zoomTo(scale, x, y); 
} else { 
    float scale = MAX_SCALE/getScale(); 
    zoomTo(scale, x, y); 
} 
} 

public void zoomTo(float scale, int x, int y) { 
if (getScale() * scale < mMinScale) { 
    return; 
} 
if (scale >= 1 && getScale() * scale > MAX_SCALE) { 
    return; 
} 
mMatrix.postScale(scale, scale); 
// move to center 
mMatrix.postTranslate(-(mWidth * scale - mWidth)/2, 
     -(mHeight * scale - mHeight)/2); 

// move x and y distance 
mMatrix.postTranslate(-(x - (mWidth/2)) * scale, 0); 
mMatrix.postTranslate(0, -(y - (mHeight/2)) * scale); 
setImageMatrix(mMatrix); 
} 

public void cutting() { 
int width = (int) (mIntrinsicWidth * getScale()); 
int height = (int) (mIntrinsicHeight * getScale()); 

imagewidth = width; 
imageheight = height; 

if (getTranslateX() < -(width - mWidth)) { 
    mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0); 
} 
if (getTranslateX() > 0) { 
    mMatrix.postTranslate(-getTranslateX(), 0); 
} 
if (getTranslateY() < -(height - mHeight)) { 
    mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight)); 
} 
if (getTranslateY() > 0) { 
    mMatrix.postTranslate(0, -getTranslateY()); 
} 
if (width < mWidth) { 
    mMatrix.postTranslate((mWidth - width)/2, 0); 
} 
if (height < mHeight) { 
    mMatrix.postTranslate(0, (mHeight - height)/2); 
} 
setImageMatrix(mMatrix); 
} 

private float distance(float x0, float x1, float y0, float y1) { 
float x = x0 - x1; 
float y = y0 - y1; 
return FloatMath.sqrt(x * x + y * y); 
} 

private float dispDistance() { 
return FloatMath.sqrt(mWidth * mWidth + mHeight * mHeight); 
} 

public void clear() { 
path.reset(); 
invalidate(); 
} 

public static void save() { 

Bitmap returnedBitmap = Bitmap.createBitmap(
     ScaleImageViewActivity.imageview.getWidth(), 
     ScaleImageViewActivity.imageview.getHeight(), 
     Bitmap.Config.ARGB_8888); 
Canvas canvas = new Canvas(returnedBitmap); 

Drawable bgDrawable = ScaleImageViewActivity.imageview.getDrawable(); 

if (bgDrawable != null) 
    bgDrawable.draw(canvas); 
else 
    canvas.drawColor(Color.WHITE); 

ScaleImageViewActivity.imageview.draw(canvas); 

ByteArrayOutputStream bs = new ByteArrayOutputStream(); 
returnedBitmap.compress(Bitmap.CompressFormat.PNG, 50, bs); 

Bitmap FinalBitmap = BitmapFactory.decodeByteArray(bs.toByteArray(), 0, 
     bs.toByteArray().length); 

ScaleImageViewActivity.imageview.setImageBitmap(FinalBitmap); 
path.reset(); 

// ScaleImageViewActivity.imageview.setImageMatrix(mMatrix); 

} 

@SuppressLint("ClickableViewAccessibility") 
@Override 
public boolean onTouchEvent(MotionEvent event) { 
if (!ScaleImageViewActivity.flag) { 

    if (mDetector.onTouchEvent(event)) { 
     return true; 
    } 
    int touchCount = event.getPointerCount(); 
    switch (event.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
    case MotionEvent.ACTION_POINTER_1_DOWN: 
    case MotionEvent.ACTION_POINTER_2_DOWN: 
     if (touchCount >= 2) { 
      float distance = distance(event.getX(0), event.getX(1), 
        event.getY(0), event.getY(1)); 
      mPrevDistance = distance; 
      isScaling = true; 
     } else { 
      mPrevMoveX = (int) event.getX(); 
      mPrevMoveY = (int) event.getY(); 
     } 
    case MotionEvent.ACTION_MOVE: 
     if (touchCount >= 2 && isScaling) { 
      float dist = distance(event.getX(0), event.getX(1), 
        event.getY(0), event.getY(1)); 
      float scale = (dist - mPrevDistance)/dispDistance(); 
      mPrevDistance = dist; 
      scale += 1; 
      scale = scale * scale; 
      zoomTo(scale, mWidth/2, mHeight/2); 
      cutting(); 
     } else if (!isScaling) { 
      int distanceX = mPrevMoveX - (int) event.getX(); 
      int distanceY = mPrevMoveY - (int) event.getY(); 
      mPrevMoveX = (int) event.getX(); 
      mPrevMoveY = (int) event.getY(); 
      mMatrix.postTranslate(-distanceX, -distanceY); 
      cutting(); 
     } 
     break; 
    case MotionEvent.ACTION_UP: 
    case MotionEvent.ACTION_POINTER_UP: 
    case MotionEvent.ACTION_POINTER_2_UP: 
     if (event.getPointerCount() <= 1) { 
      isScaling = false; 
     } 
     break; 
    } 
} else { 
    float eventX = event.getX(); 
    float eventY = event.getY(); 

    switch (event.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
     path.moveTo(eventX, eventY); 
     lastTouchX = eventX; 
     lastTouchY = eventY; 
     return true; 

    case MotionEvent.ACTION_MOVE: 

    case MotionEvent.ACTION_UP: 

     resetDirtyRect(eventX, eventY); 
     int historySize = event.getHistorySize(); 
     for (int i = 0; i < historySize; i++) { 
      float historicalX = event.getHistoricalX(i); 
      float historicalY = event.getHistoricalY(i); 
      path.lineTo(historicalX, historicalY); 
     } 
     path.lineTo(eventX, eventY); 
     break; 
    } 

    invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH), 
      (int) (dirtyRect.top - HALF_STROKE_WIDTH), 
      (int) (dirtyRect.right + HALF_STROKE_WIDTH), 
      (int) (dirtyRect.bottom + HALF_STROKE_WIDTH)); 

    lastTouchX = eventX; 
    lastTouchY = eventY; 
} 
return true; 
} 

@Override 
protected void onDraw(Canvas canvas) { 
// TODO Auto-generated method stub 
super.onDraw(canvas); 
if (ScaleImageViewActivity.flag) 
    canvas.drawPath(path, paint); 
} 

@SuppressLint("ClickableViewAccessibility") 
@Override 
public boolean onTouch(View v, MotionEvent event) { 
return super.onTouchEvent(event); 
} 

} 

//ACTIVITY 

import android.app.Activity; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 

public class ScaleImageViewActivity extends Activity implements OnClickListener { 

Button btndraw, btnzoom, btnsave; 

public static ScaleImageView imageview; 
public static boolean flag = true; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.main); 
initwidget(); 
} 

private void initwidget() { 

imageview = (ScaleImageView) findViewById(R.id.image); 

btnsave = (Button) findViewById(R.id.activity_main_save); 
btndraw = (Button) findViewById(R.id.activity_main_zoom_draw); 
btnzoom = (Button) findViewById(R.id.activity_main_zoom_zoom); 

btndraw.setOnClickListener(this); 
btnzoom.setOnClickListener(this); 
btnsave.setOnClickListener(this); 
} 

@Override 
public void onClick(View arg0) { 
// TODO Auto-generated method stub 
if (btndraw.equals(arg0)) { 
    flag = true; 
} else if (btnzoom.equals(arg0)) { 
    flag = false; 
} else if (btnsave.equals(arg0)) { 
    ScaleImageView.save(); 
} 
} 
} 
main.xml 

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
android:orientation="vertical" > 

<LinearLayout 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" > 

<Button 
    android:id="@+id/activity_main_zoom_zoom" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Zoom" /> 

<Button 
    android:id="@+id/activity_main_zoom_draw" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Draw" /> 

<Button 
    android:id="@+id/activity_main_save" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Save" /> 
</LinearLayout> 

<com.matabii.dev.scaleimageview.ScaleImageView 
android:id="@+id/image" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
android:src="@drawable/sample" /> 

</LinearLayout>