AWT Graphics G Kein Text erscheint warum?

Terep

Mitglied
Ich habe hier eine Testanwendung für den KeyListener.
Das funktioniert auch.
Was mich beschäftigt, ist Graphics g.
Warum erscheint kein Text? Keine Fehlermeldung.

Java:
import java.awt.*;
import java.awt.event.*;

public class Grafikbeispiel implements KeyListener
{

	Frame frame;
	Font font;
		
	public Grafikbeispiel()
	{
		frame = new Frame("Testgrafik");
		frame.setSize(300, 200);
		frame.setVisible(true);
		frame.addKeyListener(this);
		frame.repaint();
	}
	
	public void paint (Graphics g)
	{
		font = new Font ("SansSerif", Font.PLAIN, 36);
		g.drawString(" ESC für Beenden", 150, 150);
	}
	
	public void keyPressed(KeyEvent event) 
	{
		if (event.getKeyCode() == KeyEvent.VK_ESCAPE)
		{
			frame.setVisible(false);
			frame.dispose();
			System.exit(0);
		}
	}
	
	public void keyReleased(KeyEvent event) { }
	
	public void keyTyped(KeyEvent event) { }
}

und die TestMain:
Java:
public class StartMain 
{
	public static void main(String[] args) 
	{
		new Grafikbeispiel();
		
	}

}

Graphics kriege ich einfach nicht in den Griff.
Die Tutorials habe ich schon durch.

Entweder baue ich ein Spielfeld, dann habe ich kein Zugriff per Spiellogik.
Oder ich habe Spiellogik, aber keine Grafik.

Dies konkret habe ich noch nicht verstanden.

Daher die Frage, warum kommt hier kein Grafiktext.
Vielleicht gibt die Antwort einen Einstieg.

Terep
 
paint(Graphics g) ist eine Methode aus [JAPI]Component[/JAPI].
Das heißt, wenn die Methode etwas zeichnen soll, muss sie in einer entsprechenden Klasse, die von Component erbt, überschrieben werden.

in paint() sollten auf keinen Fall Klassen instanziiert werden!
 
Hm. Das klingt schon ein bißchen ZU drastisch 🙂 Man sollte möglichst wenig rechnen und allokieren, und die Erzeugung des Fonts steht da schon arg verloren rum, aber ums Instantiieren kommt man ja kaum drumrum.
 
aber ums Instantiieren kommt man ja kaum drumrum.
Das mag schon sein, aber auf keinen Fall in der paint()-Methode.
Ich würde den Font im Konstruktor erzeugen und in einer privaten Instanzvariable speichern.
Die paint()-Methode würde dann z.B. so aussehen:
Java:
public void paint (Graphics g) {
    super.paint(g);
    g.setFont(font);
    g.drawString("ESC für Beenden", 150, 150);
}

Anderenfalls würde bei jedem Zeichendurchgang (und das können sehr viele hintereinander sein) ein Font-Objekt erstellt werden - ziemlich unperformant.
 
Ja, das mit dem Font ist klar (vor allem weil man nicht abschätzen kann, WIE aufwändig das Erzeugen eines Fonts ist). Aber GAR keine Objekterzeugung wird schwierig... 😉 Sowas wie
g.draw(new Line2D.Double(p0, p1));
ist ja relativ unkritisch...
 
Danke für die Reaktionen. Leider noch nicht geklärt

Wenn ich wie vorgeschlagen, < super paint (g); > einsetze, kommt eine Fehlermeldung.
Die verschwindet erst, wenn ich extends Frame einsetze. Dann ist zwar der Fehler weg, die Graphics g findet wohl nicht das frame.
Java:
import java.awt.*;
import java.awt.event.*;

public class Grafikbeispiel extends Frame
{
	public Grafikbeispiel()
	{
		super("Testgrafik");
		setSize(300, 200);
		setVisible(true);
		addKeyListener(
				new KeyAdapter(){
					public void keyPressed(KeyEvent event) 
					{
						if (event.getKeyCode() == KeyEvent.VK_ESCAPE)
						{
							setVisible(false);
							dispose();
							System.exit(0);
						}
					}
				});
		repaint();  // keine Funktion
	}
	
	public void paint (Graphics g)
	{
		super.paint(g);  // nicht nötig
		g.drawString(" ESC für Beenden", 150, 150);
	}
}

Es geht natürlich sofort, wenn ich dann aus frame erbe und nicht mit new Frame progge:
Java:
import java.awt.*;
import java.awt.event.*;

public class Grafikbeispiel extends Frame
{
	public Grafikbeispiel()
	{
		super("Testgrafik");
		setSize(300, 200);
		setVisible(true);
		addKeyListener(
				new KeyAdapter(){
					public void keyPressed(KeyEvent event) 
					{
						if (event.getKeyCode() == KeyEvent.VK_ESCAPE)
						{
							setVisible(false);
							dispose();
							System.exit(0);
						}
					}
				});
		//repaint();  // keine Funktion
	}
	
	public void paint (Graphics g)
	{
	//	super.paint(g);   nicht nötig
		g.drawString(" ESC für Beenden", 150, 150);
	}
}


Die Frage, die mich treibt, warum nicht. Hier habe ich etwas nicht verstanden, was mich darin hindert Graphics g „normal“ einzusetzen. Das heißt, ich will in einer weiteren Datei Spiellogik für ein Brettspiel entwickeln und muss daher regelmäßig Graphics g bemühen.

Kann mir einer sagen, wie ich ohne extends Frame Graphics g einbinde. Muß ich wie im Hubschraubertutorial ein Interface einsetzen? Kein Fehler, leider auch keine Grafik.
Java:
import java.awt.Graphics;

public interface Drawable {
	public void drawObjects (Graphics g);
}

und dann wie folgt nutzen:
Java:
import java.awt.*;
import java.awt.event.*;

public class Grafikbeispiel extends Frame implements Drawable //ohne extends Fehler bei super.paint(g);
{
	Frame frame;
	public Grafikbeispiel()
	{
		frame = new Frame("Testgrafik");
		frame.setSize(300, 200);
		frame.setVisible(true);
		frame.addKeyListener(
				new KeyAdapter(){
					public void keyPressed(KeyEvent event) 
					{
						if (event.getKeyCode() == KeyEvent.VK_ESCAPE)
						{
							frame.setVisible(false);
							frame.dispose();
							System.exit(0);
						}
					}
				});
		frame.repaint();  // keine Funktion
	}
	
	public void drawObjects (Graphics g)
	{
		super.paint(g);
		g.drawString(" ESC für Beenden", 150, 150);
	}
}

Wie gesagt, ich habe Umsetzungsprobleme in der Art, dass ich mit Graphics g ein Spielbrett malen kann, habe aber dann Probleme, aus der Spielschleife heraus Graphics g pro Schleifengang zu aktivieren. Daher hoffe ich, dass ich hiermit der Sache auf der Spur bin.

Terep
 
Zu der Aussage
Wenn ich wie vorgeschlagen, < super paint (g); > einsetze, kommt eine Fehlermeldung.
Die verschwindet erst, wenn ich extends Frame einsetze. Dann ist zwar der Fehler weg, die Graphics g findet wohl nicht das frame.
was soll die letzte Aussage "die Graphics g findet wohl nicht das frame" bedeuten?
Wenn du super.paint(Graphics g) aufrufst, prüft der Compiler, ob in der Oberklasse eine Methode mit dem angebenen Namen und er Signatur existiert. Ohne "extends Frame" erbt deine Klasse direkt von java.lang.Object, und die hat keine paint-Methode. Deshalb gibt der Compiler eine Fehlermeldung raus.

Wenn du das "extends Frame" und "super.paint()" weglässt, kompiliert zwar der Code, aber deine paint() Methode wird nicht aufgerufen. Von wem auch?
Du brauchst anscheinend zwei Dinge:
1. Ein Grafikobjekt, was nix mit Component, Frame usw. zu tun hat, sondern einfach irgendwas auf einen Graphics zeichnet.
2. Ein spezielles Frame, Component, JComponent (letzteres würde ich dir stark empfehlene. Lass AWT AWT sein, und programmiere in Swing), welches Du in einen JFrame einbettest und welches in seiner paintComponent(Graphics g) nichts anderes macht, als das "Grafikobjekt" aus 1. zu zeichnen.
Ich habe hier mal einen kleine Demo zusammengehackt:
Java:
public class GrafikPanel extends JComponent
{
    GrafikObjekt go = new GrafikObjekt();
    public void paintComponent (Graphics g)
    {
        go.zeichneDich(g);
    }
    public static void main(String[] args){
     final JFrame frame = new JFrame("Grafik Test");
     frame.addKeyListener(
                new KeyAdapter(){
                    public void keyPressed(KeyEvent event)
                    {
                        if (event.getKeyCode() == KeyEvent.VK_ESCAPE)
                        {
                            frame.setVisible(false);
                            frame.dispose();
                            System.exit(0);
                        }
                    }
                });

     GrafikPanel gp = new GrafikPanel();
     gp.setPreferredSize(new Dimension(300, 300));
     frame.setContentPane(gp);
     frame.pack();
     frame.setVisible(true);

     GrafikPanel bsp = new GrafikPanel();
    }
}
class GrafikObjekt{
    public void zeichneDich (Graphics g)
    {
        g.drawString(" ESC für Beenden", 150, 150);
    }

}
 
Eigentlich zeichnet man nicht direkt in (J)Frames.
In AWT, dessen GUI-Komponenten inzwischen als veraltet gelten, zeichnet man in von Panel oder Component abgeleitete Klassen.
In Swing entsprechend in von JPanel oder JComponent abgeleitete Klassen. Hier wird aber nicht die paint()-, sondern die paintComponent()-Methode zum Zeichnen überschrieben.

Der super-Aufruf der Zeichenmethode der jeweiligen Basisklasse ist zwingend notwendig, um den Hintergrund der Basisklasse zu zeichnen. U.U. kommt es sonst zu unerwarteten Fehlverhalten.
 
Zuerst einmal finde ich es beeindruckend, wie schnell hier Statements kommen und der Wille zum Helfen ist spürbar, dafür danke. Leider trete ich schon eine ganze Weile auf der Stelle. Irgendwie nervig, ich kann das Spiel auf der Konsole ohne Grafik proggen, kriege es aber einfach nicht ins Bild.

Nun, ich habe versucht den Faden aufzunehmen. Die Erklärung von Harry Kane schätze ich als kompetent ein und kann es im Wesentlichen nachvollziehen aber leider nicht weiterentwickeln.

Mein Endziel ist es, ein Brettspiel nachzuprogrammieren.
Ich habe es im Quellcode angedeutet.
Java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.Graphics;

public class GrafikPanel extends JComponent
{
    GrafikObjekt go = new GrafikObjekt();
    public void paintComponent (Graphics g)
    {
        go.initFeld(g);
    }
    public static void main(String[] args){
     final JFrame frame = new JFrame("Grafik Test");
     frame.addKeyListener(
                new KeyAdapter(){
                    public void keyPressed(KeyEvent event)
                    {
                        if (event.getKeyCode() == KeyEvent.VK_ESCAPE)
                        {
                            frame.setVisible(false);
                            frame.dispose();
                            System.exit(0);
                        }
                    }
                });
 
     GrafikPanel gp = new GrafikPanel();
     gp.setPreferredSize(new Dimension(500, 200));
     frame.setContentPane(gp);
     frame.pack();
     frame.setVisible(true);
 
     GrafikPanel bsp = new GrafikPanel();
    }
}
und
Java:
import java.awt.Graphics;

class GrafikObjekt{
    public void initFeld (Graphics g)
    {
int xx, xd, yy, yd, sx, sy;
		
		xx =  10;
		yy =  40;
		sx =  80; // doppelte Feldbreite
		sy =  80; 
			
		g.drawRect (xx, yy, sx, sy);
		xx = xx + sx;
		sx =  40; // Feldbreite
		g.drawRect (xx, yy, sx, sy);
		g.drawString ("Start", xx-50, yy+15);
	
		for ( int i = 0; i < 8; i++)
		{
			g.drawString ("Pos "+ (i+1), xx+5, yy+15);
			g.drawRect (xx, yy, sx, sy);
			xx = xx + sx;
			
		}
		
    }
 }

Das Spielfeld zu malen, ist kein Problem. Aber wie bekomme ich die anfallenden Grafikarbeiten aus der Spielschleife in Laufen?
Das heißt, Spielfiguren bewegen sich, Spielfelder ändern sich usw..

Konkrete Frage. Wo und wie kann ich aus einer Spielschleife mit unterschiedlichen Grafikaufgaben ins Geschehen greifen?

Terep
 
So konkret ist das gar nicht.

Aus der "Spielschleife" heraus zeichnet man üblicherweise nicht direkt. In der Spielschleife (die vom Haupt-Thread ausgeführt wird) werden die Zustände von Objekten verändert. In der Klasse, die für's zeichnen zuständig ist, liegen entweder die Objekte selbst, oder irgendwas, was anstelle der Objekte gezeichnet werden soll. Und das macht sie - NUR von der paintComponent-Methode aus.
 
Das habe ich befürchtet. Graphics g ist wirklich keine normale Methode.
Harry Kane hat es ja in einem oberen Posting auch schon angerissen.

Ich habe mich bis jetzt viel mit der Konsole beschäftigt und erst wenig mit der GUI. In der GUI passieren all die Sachen, von denen ich vorher gesagt habe, brauche ich eigentlich nicht.

OK, zur Sache. Die Spielschleife wird in einem Thread eingebettet, Die Spielschleife ruft unter anderem die Klasse Tu_was_mit_Grafik aus. Von dort müssen die entsprechenden Infos so übergeben werden, dass Graphics g ins Arbeiten kommt.

Bildlich gesprochen bin ich nicht mehr in einer Sackgasse. Vielmehr doch, ich muss die Sackgasse verlassen und mich mehr mit der GUI beschäftigen. Das werde ich tun.
Da es keine einfache klare Lösung gibt, werde ich diesen Beitrag als erledigt markieren.

Harry Kane, danke für die inhaltliche Auseinandersetzung und Marco für die Hinweise, in welche Richtung es geht.

Terep
 

Neue Themen


Zurück
Oben