How do I make the coordinates of MotionEvent match the ones of a scaled canvas?

◇◆丶佛笑我妖孽 提交于 2019-12-24 01:15:44

问题


I have a Canvas that is scaled so everything fits better:

@Override
public void draw(Canvas c){
    super.draw(c);
    final float scaleFactorX = getWidth()/(WIDTH*1.f);
    final float scaleFactorY = getHeight()/(HEIGHT*1.f);

    if(c!=null) {
        final int savedState = c.save();

        c.scale(scaleFactorX, scaleFactorY);

        (rendering)

        c.restoreToCount(savedState);
    }

}

It scales based on these two:

public  static final int WIDTH = 856;
public  static final int HEIGHT = 1050;

Which causes the problem that the coordinates of the MotionEvent that handles touch events is not equal to the coordinates that is created with the Canvas. This causes problems when I try to check collision between the MotionEvent Rect and the Rect of a class that is based on the rendering scale. This causes the class SuperCoin's X coordinate to not be equal to MotionEvent X coordinates. Usually, MotionEvent's coordinates, both X and Y is way bigger than the screen's max size(defined by WIDTH and HEIGHT)

 @Override
public boolean onTouchEvent(MotionEvent e) {
    super.onTouchEvent(e);

    switch (MotionEventCompat.getActionMasked(e)) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_POINTER_DOWN:
            (...)
            Rect r = new Rect((int)e.getX(), (int)e.getY(), (int)e.getX() + 3, (int)e.getY() + 3);
            if(superCoins.size() != 0) {
                for (SuperCoin sc : superCoins) {
                    if (sc.checkCollision(r)) {
                        progress++;
                        superCoins.remove(sc);
                    }
                }
            }

            break;

    }

    return true;

}

And the SuperCoin:

public class SuperCoin {
    private Bitmap bm;
    public int x, y, orgY;
    Clicker cl;
    private Long startTime;
    Random r = new Random();
    public SuperCoin(Bitmap bm, int x, int y, Clicker c){
        this.x = x;
        this.y = y;
        this.orgY = y;
        this.bm = bm;
        this.cl = c;
        startTime = System.nanoTime();
        bounds = new Rect(x, y, x + bm.getWidth(), y + bm.getHeight());

    }

    private Rect bounds;

    public boolean checkCollision(Rect second){
        if(second.intersect(bounds)){
            return true;
        }
        return false;
    }


    private int velX = 0, velY = 0;


    public void render(Canvas c){

        long elapsed = (System.nanoTime()-startTime)/1000000;
        if(elapsed>50) {


            int cx;
            cx = r.nextInt(2);
            if(cx == 0){
                velX = r.nextInt(4);
            }else if(cx == 1){
                velX = -r.nextInt(4);
            }

            velY = r.nextInt(10) + 1;

            startTime = System.nanoTime();
        }

        if(x < 0) velX = +2;
        if(x > Clicker.WIDTH) velX = -2;

        x += velX;
        y -= velY;


        c.drawBitmap(bm, x, y, null);
    }
}

How can I check collision between the two different when the MotionEvent X coordinate is bigger than the screen's scaled max coordinates?

Honestly, I am not completly sure why the Rect defined in the SuperCoin class is different from the one defined in the onTouchEvent method. I'm guessing because the X and Y is permanently different between the one defined by MotionEvent and the ones defined by the scaled canvas. The Rect in the SuperCoin class goes by the width of the Bitmap it has been passed. It scales it with the width and height of the Bitmap.


回答1:


After looking through StackOverflow and Google for the past 2 days looking for something that comes close to a solution, I came over this: Get Canvas coordinates after scaling up/down or dragging in android Which solved the problem. It was really hard to find because the title was slightly misleading(of the other question)

float px = e.getX() / mScaleFactorX;
float py = e.getY() / mScaleFactorY;
int ipy = (int) py;
int ipx = (int) px;
Rect r = new Rect(ipx, ipy, ipx+2, ipy+2);

I added this as an answer and accepting it so it no longer will be an unanswered question as it is solved. The code above converts the coordinates to integers so they can be used for checking collision between the finger and the object I'm checking with




回答2:


Don't scale the canvas directly. Make a Matrix object, scale that once. Then concat that to the canvas. Then you can make an inverted matrix for your touch events.

And just make the invert matrix whenever you change the view matrix:

viewMatrix = new Matrix();
viewMatrix.scale(scalefactor,scalefactor);
invertMatrix = new Matrix(viewMatrix);
invertMatrix.invert(invertMatrix);

Then apply these two matrices to the relevant events.

@Override
    public boolean onTouchEvent(MotionEvent event) {
        event.transform(invertMatrix);

And then on the draw events, concat the matrix.

@Override
protected void onDraw(Canvas canvas) {
    canvas.concat(viewMatrix);

And you're done. everything is taken care of for you. Whatever modifications you do to the view matrix will change your viewbox and your touch events will be translated into that same scene too.

If you want to add panning or rotation, or even skew the view, it's all taken care of. Just apply that to the matrix, get the inverted matrix, and the view will look that way and the touch events will respond as you expect.



来源:https://stackoverflow.com/questions/38061734/how-do-i-make-the-coordinates-of-motionevent-match-the-ones-of-a-scaled-canvas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!