Canvas – масштабирование, изменение и масштабирование на Android

В настоящее время я реализую функцию рисования на изображении 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 не вызывает жест 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; } } 

Solutions Collecting From Web of "Canvas – масштабирование, изменение и масштабирование на Android"

Я реализовал это поведение, но несколько иначе. Я использовал матрицу для обработки всех масштабирования и прокрутки (и вращения в моем случае). Он обеспечивает аккуратный код и работает как часы. Однако я не знаю, что вызывает ваше фанковое поведение.

Храните матрицу и другой путь в качестве участников:

 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); } 

Счастливое кодирование!

Хороший способ выполнить все различные эффекты, которые вы пытаетесь сделать очень простым способом, – использовать метод холста

Canvas.drawBitmap (растровое изображение Bitmap, 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, – это простой и действительно мощный инструмент

Надеюсь это поможет.

С уважением!

Можете ли вы попытаться нарисовать дорожки только один раз и сохранить масштабирование ответственности самого холста

 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(); } 
 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>