gpt4 book ai didi

android - ZoomView Canvas 矩阵 Android

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:49:18 25 4
gpt4 key购买 nike

我正在根据在 Stack 和其他网站上找到的示例实现可缩放 View 。我确实想向 View 添加绘图功能。我能够显示路径,但缩放比例与其背后的图像相去甚远。我使用了一个示例 android 图像,我沿着形状进行追踪,路径出现在它的左侧和上方。此外,缩放会导致它移动(下图)。

我一直在尝试绘制 Canvas 的 onDraw 方法,但收效甚微。无论 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();

最佳答案

明白了!获取比例和偏移的矩阵值并相应地调整 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]));

关于android - ZoomView Canvas 矩阵 Android,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20006428/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com