Search code examples
androidimageviewontouch

Drawing a small Rect at the point where onTouch is detected


I'm performing a very small test to debug something larger, and am having trouble figuring out what's causing this issue.

I have an ImageView, a Canvas and a Bitmap. I initialize the ImageView and set it to the Bitmap, and then set the Canvas to the bitmap, as such:

_image.setImageBitmap(bitmap);
Canvas canvas = new Canvas(bitmap);

I then attach an onTouchListener to _image and listen for MotionEvent.ACTION_UP to be detected. At that point I draw a Rect on the Canvas, as such:

Rect rect = new Rect((int)event.getX(),event.getY(),event.getX()+20,event.getY()+20);
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(rect,paint);

I've tried this with bitmap images of different sizes, to smaller than the full display, to very nearly the exact size, to larger, and I can never get the Rect to draw right where my finger touches the screen. I've tried different conversions on getX(), attempting to convert to dp, from dp, used getRawX/Y() instead; basically a bunch of different solutions, none of which have worked. I've tried getting the ratio of the bitmap's size to the screen size and multiplying/dividing by that; nothing has been able to solve this very simple issue.

Quite possibly I've tried the right approach at some point and simply implemented it incorrectly. I'd really appreciate some advice on what's causing the Rect to draw where it is, what conversion is needed to get it to draw right where the touch occurs, and why. The 'why' is because I want to learn from this.

Edit: This is an approximation of what happens. The red rectangle is where the actual touch occurs, and the blue is where it is drawn. It scales the further from X=Y=0 you get. Also, I just noticed that it also scales depending on the width and height of the image you're using.

Example


Solution

  • So, I believe I've figured out the math which allows me to transform the onTouch coordinates onto the Canvas.

    The reason I really wanted to go this route (as opposed to a custom subclass for ImageView) is because I'm not really that interested in the drawing of the rectangles: I'm more interested in accurately mapping onTouch coordinates onto specific OCRed text strings in the bitmap. The reason I was performing this exercise was to figure out why the touches weren't being attributed to the correct lines of text, and I was using drawn Rects to estimate where the touches were being recorded.

    So, on to the math, which turned out to be a lot simpler than I'd feared:

    int x1 = (int)(event.getX() * ((float) canvas.getWidth() / _image.getRight()));
    int y1 = (int)(event.getY() * ((float) canvas.getHeight() / _image.getBottom()));
    

    I multiply the onTouch coordinates by the ratio of the Canvas's dimensions to the ImageView's dimensions, which I cast to a float. Painting this as follows:

    rect = new Rect(x1-10,y1-10,x1+10,y1+10);
        //I took the advice about centering the Rect around the touch for clarity
    paint = new Paint();
    paint.setColor(Color.RED);
    canvas.drawRect(rect,paint);
    

    And the Rect is exactly where the touch was, regardless of bitmap size.

    I found it interesting that it didn't cause any problems that I had called

    _image.setAdjustViewBounds(true);
    

    earlier in my program, which I did in order to remove some padding from the bitmap (which was showing up for some reason, possible due to the scaling). Also interesting is the fact that the ImageView is contained in a ScrollLayout, but even if the image is large enough to scroll though, it doesn't seem to need to take the scroll displacement into account.

    Thanks to kcoppock for taking the time to help me out, and to the other user (whose comment was deleted when he realized he had misunderstood me) for taking the time as well.