user990230
user990230

Reputation: 307

How do I draw an image from drawable onto an imageview with Canvas - Android

I have an activity which has an ImageView in it. What I want to do is be able to draw where the user touches that Imageview with an image from the drawable folder. I've read that the best way is to use Canvas, but I'm not sure where and how I integrate the onDraw method with the onTouchListener. This is what I have so far:

public class Main extends Activity
{
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      final TextView textView = (TextView)findViewById(R.id.textView);
      final ImageView image = (ImageView) findViewById(R.id.imageView2);

      //Bitmap
      Bitmap viewBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
      Canvas canvas = new Canvas(viewBitmap);
      image.draw(canvas);

      image.setOnTouchListener(new View.OnTouchListener()
      {
         @Override
         public boolean onTouch(View v, MotionEvent event)
         {
            textView.setText("Touch coordinates : " + String.valueOf(event.getX()) + "x" + String.valueOf(event.getY()));
            return false;
         }
      });
   }
}

So what I want to do is when the user touches the ImageView, an image will be drawn exactly where he touched it.

Upvotes: 4

Views: 4113

Answers (2)

zohra
zohra

Reputation: 1

I edited your class like this and it works for me

public class MyImageView extends androidx.appcompat.widget.AppCompatImageView {
    
    private Bitmap mMarker;
    private Path mPath,  circlePath ;
    private Paint  paint, circlePaint;

    private Canvas mCanvas;

    //Java constructor
    public MyImageView(Context context) {
        super(context);
        init();

    }

    //XML constructor
    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mTouches = new ArrayList<Point>();

        paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(12);

        mPath = new Path();
        Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
        circlePaint = new Paint();
        circlePath = new Path();
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setStrokeJoin(Paint.Join.MITER);
        circlePaint.setStrokeWidth(4f);

    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mMarker = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mMarker);
    }

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

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

    private void touch_move(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;

            circlePath.reset();
            circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
        }
    }

    private void touch_up() {
        mPath.lineTo(mX, mY);
        circlePath.reset();
        // commit the path to our offscreen
        mCanvas.drawPath(mPath,  paint);
        // kill this so we don't double draw
        mPath.reset();
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas c) {
        //Let the image be drawn first
        super.onDraw(c);

            c.drawBitmap(mMarker, 0, 0, paint);
        c.drawPath( mPath,  paint);
        c.drawPath( circlePath,  circlePaint);
      
    }

}

Upvotes: 0

devunwired
devunwired

Reputation: 63303

You're going to want to subclass ImageView in order to override its onDraw() method. By doing so, you can also to the custom touch handling in onTouchEvent() instead of attaching a listener. This is not a complete example, but something like the following:

public class CustomImageView extends ImageView {

    private ArrayList<Point) mTouches;
    private Bitmap mMarker;

    //Java constructor
    public CustomImageView(Context context) {
        super(context);
        init();
    }

    //XML constructor
    public CustomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mTouches = new ArrayList<Point>();
        mMarker = BitmapFactory.decodeResource(context.getResources(), R.drawable.my_marker_image);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //Capture a reference to each touch for drawing
        if(event.getAction() == MotionEvent.ACTION_DOWN) {
            mTouches.add( new Point(event.getX(), event.getY()) );
            return true;
        }

        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas c) {
        //Let the image be drawn first
        super.onDraw(c);

        //Draw your custom points here
        Paint paint = new Paint();            
        for(Point p : mTouches) {
            c.drawBitmap(mMarker, p.x, p.y, paint);
        }
    }

}

HTH!

Upvotes: 3

Related Questions