PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [Java] zeichnen innerhalb MouseListener?



BenNavis
10-06-2003, 08:40
Hi,

da es nicht möglich ist, ein ToolTip in einem bestimmten Bereich eines canvas aufpoppen zu lassen, wollte ich innerhalb meiner paint() methode einen MouseListener hinzufügen, der bei mouseclicked in einem bestimmten Bereich ein Rechteck zeichnet, in das ich dann Text reinschreibe.

addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
g2D.fillroundRect(...);
...

Mein Problem ist, dass mir der Compiler sagt, dass ich nicht aus einer anonymen klasse heraus auf mein Graphics Objekt zugreifen könnte, es sei denn ich würde es final deklarieren. Tue ich das, so bekomme ich zwar keine Fehlermeldung mehr, aber trotzdem wird nichts neues hinzugemalt.

Weiß jemand Rat?

Danke,

Ben

peschmae
10-06-2003, 12:17
ums final kommst du nicht herum (steht auch so im TIC)

allerdings macht es nicht viel sinn, referenzen final zu deklarieren, da ja das Objekt, auf das die Ref zeigt, änderbar bleibt

evtl. macht das probleme... :confused:

MfG Peschmä

anda_skoa
10-06-2003, 14:49
Ich würde sagen, einfach in der Klasse selbst den Listener implementieren.

Außerdem sollte man nur in paint zeichnen, bzw in einer davon aufgerufenen Routine, denn sonst wird es bim nächsten Paint überzeichnet.

Ciao,
_

BenNavis
10-06-2003, 15:56
OK, noch mal langsam, ich bin nicht so der Java-Fuchs :)

Ich habe den Listener in der klasse selbst implementiert.

public class Malmal extends JPanel {
...
public void paintComponent(Graphics g) {
...
final Graphics2D g2D = (Graphics2D) g;
....
addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
hier soll dann sowas kommen wie:
if (Koordinatencheck) {
g2D.drawString(...);
....

Ich habe auch schon versucht den listener in einer anderen Klasse zu implementieren, aber von da aus kann ich auch nicht auf mein g2D zugreifen. :confused:

Gruß,

Ben

anda_skoa
10-06-2003, 16:45
Original geschrieben von BenNavis
OK, noch mal langsam, ich bin nicht so der Java-Fuchs :)

Ich habe den Listener in der klasse selbst implementiert.


nein, hast du nicht :)

Das sieht so aus:


public class Malmal extends JPanel implements MouseListener

Dann die Methoden des MouseListerner Interfaces implementieren.
Da es normale Methoden sind, kannst du auf alles in der Klsse zugreifen.

Und zeichne nicht auf ein zwischengespeichertes Graphics Objekt.
Zeichnen tut man auf die Komponente nur in paint bzw paintComponent

Ciao,
_

BenNavis
10-06-2003, 17:23
OK, Du hast recht. Ich habe eine anonyme Klasse innerhalb einer Klasse, richtig?

Aber trotzdem, ich verstehe es nicht so ganz. Wenn ich die Methoden des Interfaces in meiner Malmal implementiere, dann kann ich von da aus doch auch nicht auf g2D zugreifen und so veranlassen, dass bei MouceClicked was gezeichnet wird?!

Wenn ich nur innerhalb von paint()/paintComponent() zeichnen kann, wo soll ich mir denn dann die Interface-Methoden implentieren, bzw. was steht dann da drin?


public class Malmal extends JPanel implements MouseListener

public void paintComponent(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
//hier wird gemalt
}

public void MouseClicked(MouseEvent e) {
//hier kann ich dann ja nicht sagen "male mir was, wenn mouseClicked"
}

Ich hoffe ihr verliert nicht die Geduld mit mir :rolleyes:

Ben

anda_skoa
10-06-2003, 18:18
Original geschrieben von BenNavis
OK, Du hast recht. Ich habe eine anonyme Klasse innerhalb einer Klasse, richtig?

richtig



Wenn ich nur innerhalb von paint()/paintComponent() zeichnen kann, wo soll ich mir denn dann die Interface-Methoden implentieren, bzw. was steht dann da drin?


Du speicherst zB in einer Variable die Position und machst dann update() oder repaint().
In paint() oder paintComponent() fragst du dann diese Positoon ab und zeichnest dort etwas.
Das Zeichnen passiert also zeitversetzt.

Im Eventhandler setzt du die nötigen Informationen und veranlasst ein Neuzeichnen.

Ciao,
_

itsme
10-06-2003, 21:20
Ich habe vor einiger zeit ein kleines Java Paint entwickelt. Vielleicht kannst du daraus was gebrauchen. Wenn Du willst kann ich das mal irgendwo hochladen.

BenNavis
11-06-2003, 10:11
//doppelt

BenNavis
11-06-2003, 10:12
Original geschrieben von anda_skoa
Du speicherst zB in einer Variable die Position und machst dann update() oder repaint().
In paint() oder paintComponent() fragst du dann diese Positoon ab und zeichnest dort etwas.
Das Zeichnen passiert also zeitversetzt.

Im Eventhandler setzt du die nötigen Informationen und veranlasst ein Neuzeichnen.



Ok, das habe ich begriffen und auch umgesetzt.

public class Malmal extends JPanel implements MouseListener{

int locX, locY;
....
public void mouseClicked(MouseEvent e) {
locX = e.getX();
locY = e.getY();
repaint();
}
...
public void paintComponent(Graphics g) {
.....
if (locX != 0) {
g2D.drawString("hallo", clickedX, clickedY);
repaint();
}

In meiner main hab ich dann den Listener auch noch registriert, er funktioniert auch soweit. Wenn ich in der If-Abfrage noch ein System.out.print(locX) mache, bekomme ich den Wert auch ausgegeben, wenn ich irgendwo auf das canvas klicke.
Aber gezeichnet wird leider immer noch nicht. Das sollte das repaint doch aber bewerkstelligen?!

@itsme: Wenn Du das hochladen würdest, wäre das cool. Würde mich schon mal interessieren, danke!

Gruß,

Ben

peschmae
11-06-2003, 11:10
vielleicht ergibt sich aus der Tatsache ein Problem, dass die parameterlose repaint()-Methode aus java.awt.Component kommt - im Gegensatz zu den beiden mit Parametern...

wie wärs wenn du anstelle von repaint() mal update(Graphics g) versuchst?

ausserdem solltest du in deiner paintComponent - methode noch diejenige der Parent-Klasse aufrufen (wohl am Anfang) mit

super.paintComponent(g);
(stammt von http://java.sun.com/products/jfc/tsc/articles/painting/index.html#mgr (ganz unten))

MfG Peschmä

bischi
11-06-2003, 13:55
Die update()-Methode wird in Swing nur!!! für die Doppelbufferung gebraucht! (Bsp: Animation, damit diese nicht flackert)

MfG Bischi

peschmae
11-06-2003, 14:05
gemäss obengenannter page hat Swing per default Doppelbufferung


#

Swing supports built-in double-buffering via the JComponent doubleBuffered property, and it defaults to true for all Swing components, however setting it to true on a Swing container has the general effect of turning it on for all lightweight descendents of that container, regardless of their individual property settings.
#

It is strongly recommended that double-buffering be enabled for all Swing components.


ausserdem sagt die APIdoc dazu:
Calls paint. Doesn't clear the background but see ComponentUI.update, which is called by paintComponent.

MfG Peschmä

BenNavis
11-06-2003, 16:27
Danke für Eure Unterstützung!! Leider bin ich immer noch am verzweifeln. Ich habe das Gefühl, dass das repaint nicht funktioniert.
Ich speichere in meinem mouseClicked die aktuelle Cursor Position in zwei Klassenvariablen (die Dinger außerhalb jeder Methode heißen so, oder?) und mache dann einen repaint.
Das heißt doch, dass dann bei einem click die Klassenvariablen mit dem aktuellen Wert belegt werden und dann in die paint gesprungen wird?!
Kann aber irgendwie nicht sein, denn wenn ich einen System.out.print von meinen Klassenvariablen am Anfang der paint() mache, dann sollten mir bei einem click jeweils der Wert ausgegeben werden.
Das passiert aber nicht. Ich bekomme beim Starten des Programms einmal eine 0 ausgegeben und dann passiert nie wieder eine Ausgabe, egal wieviel ich wohin clicke.
Springt er also vielleicht trotz repaint nicht in die paint?

Gruß,

Ben

bischi
11-06-2003, 17:06
@ peschmae: UPDATE: SWING UNTERSTÜTZT ES STANDARDMÄSSIG, JEDOCH IST CANVAS AWT!!!!!!!!!!!!!!!!!!

@ BenNavis: Lad dir das Java-Buch von www.javabuch.de herunter und schau dir das Kapitel 33 mal genauer an (da behandeln sie genau dein Problem).


MfG Bischi

peschmae
12-06-2003, 08:23
Original geschrieben von bischi
@ peschmae: UPDATE: SWING UNTERSTÜTZT ES STANDARDMÄSSIG, JEDOCH IST CANVAS AWT!!!!!!!!!!!!!!!!!!


das Design ist ja wirklich zum schreien :D :D

wenn ich also in den Canvas von einem Doppelgebufferten JPanel zeichne, dann ist das nicht doppelgebuffert

MfG Peschmä

bischi
12-06-2003, 08:30
Genau, denn nur das JPanel ist doppelgebuffert, nicht aber der Canvas.

MfG Bischi

peschmae
12-06-2003, 08:31
... im JPanel, der ja auch Teil davon ist...

idiotisch

MfG Peschmä

bischi
12-06-2003, 14:05
Nein! Denn sonst müsstest du sämtliche AWT-Komponenten so umschreiben, dass sie Doppelbufferung beherrschen (selbst wenn JPanel doppelgebuffert ist!).

MfG Bischi:p

peschmae
12-06-2003, 14:08
als ob du eine Ahnung vom AWT-Design hättest....

im übrigen soll Sun ruhig mal a bisserl arbeiten...

MfG Peschmä

bischi
12-06-2003, 14:14
als ob du eine Ahnung vom AWT-Design hättest....

Wie willst du das wissen?

MfG Bischi

PS: Besser Sun macht das ganze schneller, als das standardmässig doppelbufferung verwendet wird...

peschmae
12-06-2003, 14:41
das sieht man dir doch an :p

ausserdem lieber ein gutes konsistentes Design, als keines...

und: schneller machen tut Sun schon seit es Java gibt - bei jeder neue version ist von 80% der Geschwindigkeit von C++ - Progs die rede :D

MfG Peschmä

peschmae
12-06-2003, 15:23
Fortsetzung:


Original geschrieben von bischi
Nein! Denn sonst müsstest du sämtliche AWT-Komponenten so umschreiben, dass sie Doppelbufferung beherrschen (selbst wenn JPanel doppelgebuffert ist!).


Du scheinst mir recht inkonsistent zu sein...




Die update()-Methode wird in Swing nur!!! für die Doppelbufferung gebraucht!

ich dachte, das Canvas-Teil sei AWT!? Und woher kommt dann Update für die AWT-Komponenten? Wenn die keine DB könnnen? Oder bedeutet das für Swing und AWT was verschiedenes?

egal....

Das Thema war wohl ursprünglich ein recht fest anderes

MfG Peschmä

peschmae
12-06-2003, 19:05
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

import javax.swing.*;

public class Tooltip extends JFrame {
public Tooltip() {
getContentPane().add(new TooltipPanel());
setSize(500, 300);
}

public static void main(String[] args) {
JFrame jframe = new Tooltip();
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLO SE);
jframe.show();
}

class TooltipPanel extends JPanel {
private int x = 0, y = 0;
private final int BORDER = 5;

private Color ttColor = new Color(255, 255, 231);

TooltipPanel() {
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
System.out.println("MouseClickedEvent");
x = e.getX();
y = e.getY();
repaint();
}
}
);
}

public void paintComponent(Graphics g) {
super.paintComponent(g);

g.drawString("Test", 10, 20);

if (x != 0 || y != 0) {
String s = "ToolTIPPERLI!";

g.setColor(ttColor);
Rectangle2D bounds = getFontMetrics(getFont()).getStringBounds(s, g);
int width = (int)bounds.getWidth() +2*BORDER;
int height = (int)bounds.getHeight()+2*BORDER;

g.fillRect(x-BORDER, y-height+BORDER, width, height);

g.setColor(Color.BLACK);
g.drawRect(x-BORDER, y-height+BORDER, width, height);

g.drawString(s, x, y);
}
}
}
}



das funktioniert soweit recht gut.

Der einzige Haken ist, dass ein User noch klicken muss, um in den Genuss des gehaltvollen Tooltips zu kommen (und dass java manchmal nicht mitmag mit klicks zählen und repainten, wenns schnell geht)

Mit einem MouseMotionListener liese sich die Sache evtl. als "echter" Tooltip realisieren - ist die einzige, allerdings recht komplizierte Methode, die mir einfällt:

- der MouseMotionListener setzt bei Mausbewegung (mouseMove()) eine Variable auf 10 (der schreib/lesezugriff erfolgt über get/setter Methoden die _synchronized_ sein müssen
- ein separater Thread prüft z.B. alle 2 Sekunden die Variable und setzt sie um 2 runter. Ist sie < 0, so wird der Tooltip angezeigt (da ja die Maus in den letzten 10 Sekunden nicht verschoben wurde).
Ist die Variable < -20 wird der Tooltip wieder ausgeblendet

der Thread läuft nur dann, wenn sich die Maus auch im Widget befindet. Er wird über einen MouseListener (mouseExited/mouseEntered) gestartet

MfG Peschmä

BenNavis
13-06-2003, 10:21
peschmae, Du bist mein Held! :) :)

Ich hab die Geschichte anhand Deines Beispiels für mich nachbauen können. Scheinbar war bei mir das Problem, dass ich den MouseListener nicht in den Konstruktor geschrieben habe.
Wie auch immer, es läuft! :)

Vielen Dank,

Ben

peschmae
13-06-2003, 10:51
gut :D

wie machst du das denn jetzt? Muss der User für die tooltips einfach immer klicken? Ist doch eher unpraktisch

MfG Peschmä

BenNavis
13-06-2003, 11:56
In diesem Fall passt das mit dem klicken sehr gut. Ich wollte erst per klick auf einen Button Hilfe-Text an bestimmte Stellen einblenden. Jetzt kann ich sie kontextsensitiv einblenden, ist eindeutig die schönere Lösung.
Per mouseover wäre glaub ich zu viel.

Gruß,

Ben