Search code examples
androidandroid-drawabletouch-event

How to create event for specific Drawable?


I have to make an event when I touch cards with diamonds. This because the aim is to get a score when I touch them. Nothing has to happen if I touch out of the card (eg. background or card with the oval).

I have gone through the onTouchEvent() method but it is general on all over the screen, not only a single Drawable.

GameView.java

package com.example.game;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.Arrays;
import java.util.Random;

public class GameView extends View {

    private static final String TAG = "TAG_GameView";
    private final Drawable diamond;
    private final Drawable circle;

    private final boolean[] columnFree;
    private final Paint background;

    int column;
    int a;
    int b;

    int[] x;
    int[] y;
    int[] speed;
    int[] type;

    public GameView(Context context) {
        super(context);
        diamond = context.getResources().getDrawable(R.drawable.diamond);
        circle = context.getResources().getDrawable(R.drawable.circle);

        background = new Paint();

        column = 4;
        x = new int[column];
        y = new int[10*column];
        speed = new int[column];
        type = new int[10*column];
        columnFree = new boolean[column];

        Arrays.fill(y, 9999);
        Arrays.fill(type, 0);
        for (int i=0; i<speed.length; i++) {
            speed[i]=10*(1+(new Random().nextInt(2)));
        }
    }

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

        background.setShader(new LinearGradient(0,0,0,getHeight(),Color.BLUE,Color.CYAN, Shader.TileMode.CLAMP));
        canvas.drawPaint(background);

        a = getWidth()*8/(9*column+1);
        b = a*diamond.getIntrinsicHeight()/diamond.getIntrinsicWidth();

        for (int i = 0; i < x.length; i++) {
            x[i] = a/8*(i+1) + a*i;
            columnFree[i] = true;
        }

        for (int i = 0; i < y.length; i++) {
            y[i] += speed[i%column];
            if (y[i]<b) {
                columnFree[i%column]=false;
            }
        }

        for (int i = 0; i < y.length; i++) {
            if ((y[i] > getHeight() && columnFree[i % column]) == true) {
                //For 40% chance of true
                if (new Random().nextInt(50) < 1) {
                    y[i] = -b;
                    columnFree[i%column] = false;
                    type[i] = new Random().nextInt(2);
                    Log.i(TAG, String.valueOf(type[i]));
                }
            } else {
                if (type[i]==0){
                    diamond.setBounds(x[i%column], y[i],x[i%column]+a,y[i]+b);
                    diamond.draw(canvas);
                } else {
                    circle.setBounds(x[i%column], y[i],x[i%column]+a,y[i]+b);
                    circle.draw(canvas);
                }
            }
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Arrays.fill(speed, 0);
        }
        return super.onTouchEvent(event);
    }
}

enter image description here


Solution

  • As @CommonsWare suggested you need to keep track of all the cards positions currently present on the screen(or just all of them) and in onTouchEvent() check whether the event took place on diamond card

    Track cards like this:

    Map<RectF, Drawable> cards = new HashMap();
    
    // in onDraw method when you draw card also add it to that map
    
    @Override
    protected void onDraw(Canvas canvas){
    //...
    
        if (type[i]==0){
            diamond.setBounds(x[i%column], y[i],x[i%column]+a,y[i]+b);
            diamond.draw(canvas);
            cards.put(new RectF(x[i%column], y[i],x[i%column]+a,y[i]+b), diamond)
        } else {
            circle.setBounds(x[i%column], y[i],x[i%column]+a,y[i]+b);
            circle.draw(canvas);
            cards.put(new RectF(x[i%column], y[i],x[i%column]+a,y[i]+b), diamond)
        }
    //...
    }
    
    

    and then in onTouchEvent()

    int points = 0;
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //...
        float x = event.getX();
        float y = event.getY();
        for (Map.Entry<RectF, Drawable> entry : cards.entrySet()) {
            if (entry.getKey().contains(x, y) && entry.getValue() == diamond) 
                points++;
        }
        //...
    }
    

    Also you may adjust the usage to score points for one card only once by introducing some boolean value(to check whether it was clicked) and card id(to distinguish it from other cards) and storing them along with the card rectangle and Drawable.

    Or you may just clean the column from cards from the position of the clicked diamond card and till the end of the column(so it won't be redrawn on next frame)

    Hope it helps