Масштабирование Android, перетаскивание, рисование в ImageView

У меня проблема с рисованием и масштабированием ImageView. Помоги мне, пожалуйста..

Когда я что-то рисую, а затем перетаскиваю или масштабирую изображение - рисунок остается на месте, как вы можете видеть на снимке экрана. А мне нужно просто рисовать на картинке, и иметь возможность масштабировать и перетаскивать эту картинку.

введите описание изображения здесь введите здесь описание изображения

У меня есть собственный ImageView с этими действиями:

 public class DrawingView extends ImageView {
// onTouch
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;

private Path mPath;

private boolean zoomEnabled = true;

private Paint drawPaint, canvasPaint;
//initial color
private int paintColor = 0xFF660000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;

private static final int INVALID_POINTER_ID = -1;

private float mPosX;
private float mPosY;

private float mLastTouchX;
private float mLastTouchY;
private float mLastGestureX;
private float mLastGestureY;
private int mActivePointerId = INVALID_POINTER_ID;

private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;

public DrawingView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    setupDrawing();
}

public DrawingView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    setupDrawing();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    super.onSizeChanged(w, h, oldw, oldh);
    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    drawCanvas = new Canvas(canvasBitmap);

}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    final int action = ev.getAction();
    mScaleDetector.onTouchEvent(ev);
    if (zoomEnabled) {
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                if (!mScaleDetector.isInProgress()) {
                    final float x = ev.getX();
                    final float y = ev.getY();

                    mLastTouchX = x;
                    mLastTouchY = y;
                    mActivePointerId = ev.getPointerId(0);
                }
                break;
            }
            case MotionEvent.ACTION_POINTER_1_DOWN: {
                if (mScaleDetector.isInProgress()) {
                    final float gx = mScaleDetector.getFocusX();
                    final float gy = mScaleDetector.getFocusY();
                    mLastGestureX = gx;
                    mLastGestureY = gy;
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: {

                // Only move if the ScaleGestureDetector isn't processing a gesture.
                if (!mScaleDetector.isInProgress()) {
                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(pointerIndex);
                    final float y = ev.getY(pointerIndex);

                    final float dx = x - mLastTouchX;
                    final float dy = y - mLastTouchY;

                    mPosX += dx;
                    mPosY += dy;

                    invalidate();

                    mLastTouchX = x;
                    mLastTouchY = y;
                } else {
                    final float gx = mScaleDetector.getFocusX();
                    final float gy = mScaleDetector.getFocusY();

                    final float gdx = gx - mLastGestureX;
                    final float gdy = gy - mLastGestureY;

                    mPosX += gdx;
                    mPosY += gdy;

                    invalidate();

                    mLastGestureX = gx;
                    mLastGestureY = gy;
                }

                break;
            }
            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
            case MotionEvent.ACTION_CANCEL: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
            case MotionEvent.ACTION_POINTER_UP: {

                final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                } else {
                    final int tempPointerIndex = ev.findPointerIndex(mActivePointerId);
                    mLastTouchX = ev.getX(tempPointerIndex);
                    mLastTouchY = ev.getY(tempPointerIndex);
                }
                break;
            }
        }

        return true;
    } else {
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                touchStart(ev.getX(), ev.getY());
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touchMove(ev.getX(), ev.getY());
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touchUp();
                invalidate();
                break;
        }
        return true;
    }
}

@Override
public void onDraw(Canvas canvas) {
    canvas.save();
    canvas.translate(mPosX, mPosY);

    if (mScaleDetector.isInProgress()) {
        canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
    }
    else{
        canvas.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY);
    }
    super.onDraw(canvas);
    canvas.restore();

    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
    canvas.drawPath(mPath, drawPaint);
}

private class ScaleListener extends ScaleGestureDetector.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, 10.0f));

        invalidate();
        return true;
    }
}

private void setupDrawing(){
    mPath = new Path();
    drawPaint = new Paint();
    drawPaint.setColor(paintColor);
    drawPaint.setAntiAlias(true);
    drawPaint.setStrokeWidth(20);
    drawPaint.setStyle(Paint.Style.STROKE);
    drawPaint.setStrokeJoin(Paint.Join.ROUND);
    drawPaint.setStrokeCap(Paint.Cap.ROUND);
    canvasPaint = new Paint(Paint.DITHER_FLAG);
}

public void setZoomEnabled(boolean b){
    this.zoomEnabled = b;
}

private void touchStart(float x, float y) {
    // mPath.reset();
    mPath.moveTo(x, y);
    mX = x;
    mY = y;
}

private void touchMove(float x, float y) {
    float dx = Math.abs(x - mX);
    float dy = Math.abs(y - mY);
    if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
        mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
        mX = x;
        mY = y;
    }
}

private void touchUp() {
    mPath.lineTo(mX, mY);
    // commit the path to our offscreen
    drawCanvas.drawPath(mPath, drawPaint);
    // kill this so we don't double draw
    mPath.reset();
}

И активность:

 public class DrawActivity extends Activity{

DrawingView image;

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

    final Paint paint = new Paint();
    paint.setColor(MyApp.getColorRes(R.color.primary));
    paint.setStrokeWidth(10);

    image = (DrawingView) findViewById(R.id.image);

    Bitmap bmp = BitmapFactory.decodeByteArray(
            getIntent().getByteArrayExtra("byteArray"), 0, getIntent().getByteArrayExtra("byteArray").length);


    Bitmap alteredBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
    final Canvas canvas = new Canvas(alteredBmp);
    canvas.drawBitmap(bmp, 0, 0, paint);
    image.setImageBitmap(alteredBmp);

    ImageButton paintBtn = (ImageButton) findViewById(R.id.paint_btn);
    paintBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View button) {
            button.setSelected(!button.isSelected());
            image.setZoomEnabled(!button.isSelected());
        }
    });

}

person Paul Chernenko    schedule 18.11.2015    source источник


Ответы (2)


В вашем onDraw() вы рисуете свой путь непосредственно с помощью холста представления. Вместо этого нарисуйте путь к canvasBitmap через drawCanvas. После этого ваш canvasBitmap будет содержать как изображение, так и рисунок. Теперь, когда вы масштабируете или перемещаете, оба изменяются соответствующим образом, поскольку теперь они находятся в одном растровом изображении.

Вместо этого:

canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(mPath, drawPaint);

Сделайте что-то вроде этого:

drawCanvas.drawPath(mPath, drawPaint);
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
person Henry    schedule 19.11.2015
comment
Спасибо!! Ты был прав! Также мне пришлось поместить эти строки перед canvas.restore(); - person Paul Chernenko; 19.11.2015
comment
Когда я рисую в zoomedview, он рисует по неправильной координате, как решить ?? - person Arjun; 20.07.2017
comment
Вы можете сделать это двумя способами. Сначала масштабируйте холст до 0, затем нарисуйте, а затем вернитесь к исходному коэффициенту масштабирования. Или вы можете масштабировать свою координату, а затем рисовать с этой координатой. - person Henry; 20.07.2017

Вот полный исходный код Zoom,Drag,Touchable ImageView. Его работа плавная при выполнении перетаскивания и масштабирования на изображении.

пакет com.admin.imagemovementzoom;

import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.squareup.picasso.Picasso;


public class FullmageActivity extends AppCompatActivity implements View.OnTouchListener {

    private ImageView img_fullimage;
    private String picturePath;


    private static final String TAG = "Touch";
    // These matrices will be used to move and zoom image
    Matrix matrix = new Matrix();
    Matrix savedMatrix = new Matrix();

    // 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;

    // Remember some things for zooming
    PointF start = new PointF();
    PointF mid = new PointF();
    float oldDist = 1f;

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fullmage);

        picturePath = getIntent().getStringExtra("image");

        FindId();

        Log.d("adaa", "file://" + picturePath);

        Picasso.get().load("file://" + picturePath).into(img_fullimage);

        img_fullimage.setOnTouchListener(this);
        img_fullimage.setScaleType(ImageView.ScaleType.MATRIX);

    }


    private void FindId() {
        img_fullimage = findViewById(R.id.img_full);
    }


    /**
     * Called when a touch event is dispatched to a view. This allows listeners to
     * get a chance to respond before the target view.
     *
     * @param v     The view the touch event has been dispatched to.
     * @param event The MotionEvent object containing full information about
     *              the event.
     * @return True if the listener has consumed the event, false otherwise.
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        ImageView view = (ImageView) v;

        // Dump touch event to log
        dumpEvent(event);

        // Handle touch events here...
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                start.set(event.getX(), event.getY());
                Log.d(TAG, "mode=DRAG");
                mode = DRAG;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                Log.d(TAG, "oldDist=" + oldDist);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                    Log.d(TAG, "mode=ZOOM");
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                Log.d(TAG, "mode=NONE");
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == DRAG) {
                    // ...
                    matrix.set(savedMatrix);
                    matrix.postTranslate(event.getX() - start.x,
                            event.getY() - start.y);
                } else if (mode == ZOOM) {
                    float newDist = spacing(event);
                    Log.d(TAG, "newDist=" + newDist);
                    if (newDist > 10f) {
                        matrix.set(savedMatrix);
                        float scale = newDist / oldDist;
                        matrix.postScale(scale, scale, mid.x, mid.y);
                    }
                }
                break;
        }

        view.setImageMatrix(matrix);
        return true; // indicate event was handled
    }

    /**
     * Show an event in the LogCat view, for debugging
     */
    private void dumpEvent(MotionEvent event) {
        String names[] = {"DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
                "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?"};
        StringBuilder sb = new StringBuilder();
        int action = event.getAction();
        int actionCode = action & MotionEvent.ACTION_MASK;
        sb.append("event ACTION_").append(names[actionCode]);
        if (actionCode == MotionEvent.ACTION_POINTER_DOWN
                || actionCode == MotionEvent.ACTION_POINTER_UP) {
            sb.append("(pid ").append(
                    action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
            sb.append(")");
        }
        sb.append("[");
        for (int i = 0; i < event.getPointerCount(); i++) {
            sb.append("#").append(i);
            sb.append("(pid ").append(event.getPointerId(i));
            sb.append(")=").append((int) event.getX(i));
            sb.append(",").append((int) event.getY(i));
            if (i + 1 < event.getPointerCount())
                sb.append(";");
        }
        sb.append("]");
        Log.d(TAG, sb.toString());
    }

    /**
     * Determine the space between the first two fingers
     */
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    /**
     * Calculate the mid point of the first two fingers
     */
    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);
    }
}
person Bhavik Nathani    schedule 20.08.2018