我目前正在webview图片(下面的大象)上实现绘图功能。我没有绘制它的问题,但缩放功能在绘图上做了一些时髦的东西(下面的第二张图片)。当我放大时,绘图不会缩放而是转换。缩放绘图也不起作用。我的代码如下:
@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;
}
}
答案 0 :(得分:2)
我已经实现了这种行为,但方式略有不同。我使用矩阵来处理所有缩放和滚动(以及旋转,在我的情况下)。它使得代码整洁,像钟表一样工作。不过,我不知道是什么导致了你的时髦行为。
将Matrix和其他路径存储为类成员:
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);
}
快乐的编码!
答案 1 :(得分:0)
以一种非常简单的方式完成所有不同效果的好方法是使用canvas方法
canvas.drawBitmap(位图位图,Rect src,Rect dst,Paint paint);
此方法允许您获取整个源位图或只是它的一小部分样本(Rect src),并根据需要投影(Rect 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上做任何你想要的效果,是一个简单而且非常强大的工具
希望这有帮助。
问候!
答案 2 :(得分: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();
}
答案 3 :(得分: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>
答案 4 :(得分:0)
看看这个tutorial。在这里,作者使用两个Matrix对象来管理转换和缩放。我建议您使用矩阵,否则计算新坐标可能会很棘手且乏味。您的问题是由于未正确管理翻译引起的。注意:在本教程的OnDraw()内部,它错过了canvas.concat(matrix)
,放在canvas.drawBitmap(...)
之后