Auf eine Zeichnung im Canvas reagieren

Bitte aktiviere JavaScript!
Hallo!

Ich habe vor einigen Jahren mal ein Spiel entwickelt und das ganze in Flash und HTML/JS realisiert und möchte das nun auch auf Java übertragen. Allerdings stehe ich vor folgender Hürde: Sowohl in Flash als auch in HTML lassen sich einfach einfache Objekte (MovieClips / DIV-Boxen) erzeugen, die auf ein Event reagieren können. Ich will aber kein Parkett aus JButtons in Java haben, deswegen will ich das mit Graphics und einem Canvas realisieren. Jetzt stellt sich mir eine Frage: Kann ein Objekt auf einem Canvas mit zB einem Mausklick interagieren?
Präzise geht es darum, dass ich mittels eines Mausklicks auf ein Canvas die Farbe des darin aufgemalten Rectangles eruieren will.
Wie wäre dafür der beste (und eleganteste!) Lösungsansatz?

LG
Matze
 
A

Anzeige


Vielleicht hilft dir dieser Kurs hier weiter: (hier klicken)
Im Endeffekt würdest du auf dem Canvas wohl einen MouseListener registrieren und dann schauen ob der Mausklick in dem Rechteck liegt (Sprich Koordinaten anschauen/vergleichen)
 
Ja das schon aber da reagiert das Programm nict auf die Zeichnungen sondern auf das ganze Canvas, das in 3 Teile unterteilt ist und dann abhängig von der Mausposition die oberste Scheibe markiert.
Das ist doch auch nichts anderes. Die Frage ist doch nur, was die "Canvas-Klasse" mit dem Ereignis anstellt: reagiert es auf den Mausklick selbst oder gibt es das Ereignis an das angeklickte Objekt weiter. Je nachdem wie weit Du gehst, landest Du ggf. bei (J)Component. Die Grenzen sind da fließend.
 
Das ist doch auch nichts anderes. Die Frage ist doch nur, was die "Canvas-Klasse" mit dem Ereignis anstellt: reagiert es auf den Mausklick selbst oder gibt es das Ereignis an das angeklickte Objekt weiter. Je nachdem wie weit Du gehst, landest Du ggf. bei (J)Component. Die Grenzen sind da fließend.
Es wird so sein, dass die jeweiligen Objekte damit interagieren werden, weswegen ich auch schon in betracht gezogen hab das ganze gar nicht zu zeichnen sondern mit JComponents umzusetzen, von denen ich weiß, dass sie spezifisch auf Ereignisse reagieren und sich wandeln können. Dabei hab ich allerdings die Sorge, um das nicht auf die Performance gehen könnte, wenn ich da 100 JComponents hab, die dann auch noch etwas machen sollen?
 
Ach. Du hast das Canvas doch irgendwann irgendwo gezeichnet. Warum merkst du dir das nicht einfach in einer Liste (wenn es mehrere sind) und schaust dann nach? Du must doch nicht das Canvas etwas fraagen das du ihm selber bestimmt hast....
 
Es wird so sein, dass die jeweiligen Objekte damit interagieren werden, weswegen ich auch schon in betracht gezogen hab das ganze gar nicht zu zeichnen sondern mit JComponents umzusetzen, von denen ich weiß, dass sie spezifisch auf Ereignisse reagieren und sich wandeln können. Dabei hab ich allerdings die Sorge, um das nicht auf die Performance gehen könnte, wenn ich da 100 JComponents hab, die dann auch noch etwas machen sollen?
Was sollten die denn "machen"?

Du kannst natürlich hergehen und selbst "Komponenten" entwerfen, die dann etwas leichtgewichtigter sind. Das hängt aber davon ab, was das Ding können soll. Wenn Du z. B. wirklich nur Shapes haben willst, könntest Du folgendes machen:
Java:
interface ClickableShape extends Shape {
    void processMouseEvent(MouseEvent e);
}
Dein Canvas muss dann bei einem Mouse-Ereignis einfach das betreffende Shape heraussuchen und processMouseEvent aufrufen. Aber dann geht es schon los: wie sollen die Shapes denn gezeichnet werden? Hm... da könnte man auf die Idee kommen und statt Shapes zu erweitern einfach das paint-Ereignis auch noch weitergeben:
Java:
interface Clickable {
    void processMouseEvent(MouseEvent e);
    void draw(Graphics g);
}
Und jetzt wollen wir noch dieses und jenes und schon sind wir bei JComponent angelangt.
 
Dieser Lösungsansatz sieht gar nicht mal so schlecht aus, ich denke das könnte das sein, was ich brauche.
Im Prinzip nichts Aufregendes: Es wird ein Feld mit quadratischen Kacheln generiert. Bei einem Klick auf die Kachel soll die Farbe der Kachel herausgefunden und daraufhin sämtliche Farben der Kacheln zufällig geändert werden. Manko dabei: Es muss zwingend mit einer Farbverlauf-Animation geschehen.

Das Ganze wäre mit JComponents natürlich ganz easy umzusetzen allerdings suche ich eben nach der elegantesten und vor allem performantesten Lösung dafür. Deswegen war die Idee das Ganze zeichnen zu lassen.
 
Von wie vielen Objekten auf dem Canvas reden wir eigentlich? Sind die frei verschiebbar und können sie sich überlappen? Warum nicht JavaFX nehmen und Shapes (Circle, Rectangle, SVGPath etc.) auf einem Pane platzieren und die vorhanden APIs verwenden - niemand redet davon, dass man da alles mit Buttons zupflastern muss, aber so musst du ständig das Cavas neu zeichnen, dir merken, welches Objekt in welchem Bereich liegt, diesen immer mit der Mausklick-Posittion abgleichen. Für mich klingt es einfach zu sehr nach Overkill im Moment - aber das ist nur meine Meinung...
 
Mal ein Beispiel. Achtung: die Datei ist ggf. etwas wirr und enthält aus Gründen der Faulheit Darstellung im Forum mehr als nur die Komponente :) Sprich: ab main gehört das nicht mehr zur Komponente selbst.

Java:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.*;
import javax.swing.*;
import java.util.Random;
import java.util.function.Consumer;

public class TilesPanel extends JComponent {

    private Color[] colors;    
    private int tilesPerRow;
    private double balance;

    public TilesPanel(Color[] colors, int tilesPerRow) {
        this.colors = colors;
        this.tilesPerRow = tilesPerRow;
    }

    public int getTileCount() {
        return colors.length;
    }

    public Color[] getColors() { return colors; }    

    public Color getTileColor(int ix) {
        return colors[ix];
    }

    public void setColors(Color[] colors) {
        this.colors = colors;
        repaint();
    }        

    public int getTileIndex(Point p) {
        Point tile = viewToTile(p);
        return tilesPerRow * tile.y + tile.x;
    }

    public Point viewToTile(Point p) {
        Rectangle2D box = getDrawingBox();
        double x = p.getX() - box.getX();
        double y = p.getY() - box.getY();

        Rectangle2D size = getTileRect();
        int xTile = (int)(x / size.getWidth());
        int yTile = (int)(y / size.getHeight());

        return new Point(xTile, yTile);
    }

    public Rectangle2D getDrawingBox() {
        Insets insets = getInsets();
        int x = insets.left;
        int y = insets.top;
        double w = getWidth() - insets.left - insets.right;
        double h = getHeight() - insets.top - insets.bottom;

        return new Rectangle2D.Double(x, y, w, h);
    }

    public Rectangle2D getTileRect() {
        Rectangle2D box = getDrawingBox();

        double tileWidth = box.getWidth() / tilesPerRow;
        double tileHeight = box.getHeight() / getRows();
        return new Rectangle2D.Double(0, 0, tileWidth, tileHeight);
    }

    private int getRows() {
        return (colors.length + tilesPerRow - 1) / tilesPerRow;
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
 
        Rectangle2D tile = getTileRect();
        double tileWidth = tile.getWidth();
        double tileHeight = tile.getHeight();

        g2.translate(getInsets().left, getInsets().top);

        for (int i = 0; i < colors.length; i++) {
            drawTile(g2, tile, i);
            g2.translate(tileWidth, 0);

            if ((i+1) % tilesPerRow == 0) {
                g2.translate(-tileWidth*tilesPerRow, tileHeight);
            }
        }
    }

    private void drawTile(Graphics2D g, Rectangle2D tile, int ix) {
        g.setColor(colors[ix]);
        g.fill(tile);
        g.setColor(Color.BLACK);
        g.draw(tile);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            Random rand = new Random(1L);
            Color[] colors = randomColors(400, rand);
            Animation animation = new Animation();

            TilesPanel panel = new TilesPanel(colors, 20);
            panel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    int ix = panel.getTileIndex(e.getPoint());

                    Color[] destColors = randomColors(colors.length, rand);
                    destColors[ix] = panel.getTileColor(ix);

                    ColorTransition colorTransition = new ColorTransition(
                            panel.getColors(), destColors);

                    animation.start(500, v -> panel.setColors(colorTransition.perform(v)));
                }
            });

            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.add(panel);
            frame.setSize(600, 400);
            frame.setVisible(true);

        });
    }

    public static Color[] randomColors(int num, Random rand) {
        Color[] colors = new Color[num];
        for (int i = 0; i < colors.length; i++) {
            colors[i] = new Color((int)(rand.nextInt()));
        }        
        return colors;
    }

    private static class ColorTransition {
        private Color[] source;
        private Color[] dest;

        public ColorTransition(Color[] source, Color[] dest) {
            this.source = source;
            this.dest = dest;
        }

        public Color[] perform(double value) {
            Color[] colors = new Color[source.length];
            for (int i = 0; i < colors.length; i++) {
                colors[i] = transformedColor(i, value);
            }
            return colors;
        }        

        private Color transformedColor(int ix, double value) {
            int red = dest[ix].getRed() - source[ix].getRed();
            int green = dest[ix].getGreen() - source[ix].getGreen();
            int blue = dest[ix].getBlue() - source[ix].getBlue();
            double factor = value * value;
            return new Color(
                (int)(source[ix].getRed() + red * factor),
                (int)(source[ix].getGreen() + green * factor),
                (int)(source[ix].getBlue() + blue * factor));
        }

    }

    private static class Animation {
        private Timer timer;

        public void start(long duration, Consumer<Double> consumer) {
            if (timer == null) {
                long start = System.currentTimeMillis();
                timer = new Timer(10, e -> processTimerEvent(start, duration, consumer));
                timer.setRepeats(true);
                timer.start();
            }
        }

        public void stop() {
            if (timer != null) {
                timer.stop();
                timer = null;
            }
        }    

        private void processTimerEvent(long start, long duration, Consumer<Double> consumer) {
            double value = (System.currentTimeMillis() - start) / (double) duration;
            if (value >= 1.0) {
                consumer.accept(1.0);
                stop();
            } else {
                consumer.accept(value);
            }
        }
    }
}
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben