Wie kann meine Klasse in ein JPanel zeichnen?

droink

Mitglied
Hallo,

ich möchte in meine Klasse eine Methode schreiben die es mir erlaubt bestimmte kreise und rechtecke in ein panel zu zeichnen. Hierbei soll allerdings das panel in einer anderen klasse erstellt werden und lediglich an die methode übergeben werden. Das ganze soll in etwa so aussehen:

public class Slot extends ISlot {

public Slot(){}

public void draw(Jpanel panel){
...
...
...
}
}

Bisher hab ich es nur hingekriegt direkt eine JPanel Klasse zu erstellen und dann über das überschreiben der paint(graphics g) methode in das panel zu zeichnen. Weiss aber nicht wie das gehen soll wenn ich lediglich ein JPanel übergeben bekomme. Hoffe jemand kann mir helfen.

Danke im voraus!
 
Naja bisher hab ich das Zeichnen in einem JPanel so realisiert, dass ich eine eigene Klasse erstellt habe welche von JPanel geerbt hat. In dieser Klasse habe ich dann die Methode paintComponent überschrieben. Das ganze sah in etwa so aus:

Java:
class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(10, 10, 10, 10);
// usw....
}

Jetzt habe ich allerdings eine eigene Klasse die lediglich ein bereits vorhandenes JPanel übergeben bekommt. Ich weiss nicht wie ich dann in dieses Panel eine Rechteck oder so zeichnen soll. Sozusagen ich weiss nicht wie ich auf die paintComponent Methode meines Panels zugreife um ihr zusagen sie soll ein weiteres Rechteck hinzufügen. Das ganze sieht so aus:

Java:
 class MyClass {

public void drawMyClass(JPanel jpanel){
// Hier will ich jetzt sagen, dass in jpanel z.b. noch ein Rechteck eingezeichnet werden soll
}
}
Hoffe es ist jetzt ein bisschen verständlicher, kann auch sein, dass ich total auf dem Schlauch stehe und die Lösung ist total simpel...
 
Habe mir ein Panel gebaut und jetzt mal folgendes ausprobiert:

Java:
public void drawtest(JPanel panel){
		Graphics g = panel.getGraphics();
		Graphics2D g2 = (Graphics2D)g;
		g2.draw(new Line2D.Double(30,30,80,80)); //hier kommt eine nullpointer exception
		panel.paint(g2);
		
	}

Jetzt kommt allerdings in der Zeile g2.draw... eine nullpointer exception. Weiss jemand was ich falsch mache?
 
Na Du rufst einfach innerhalb der Paint-Funktion der Klasse, die das Panel erstellt/beinhaltet, die besagte Funktion der anderen Klasse, mit "Graphics g" als Parameter, auf.
 
Hmm... das JPanel muss sich schon selber zeichnen, anders geht das nicht.
Der Standardweg wäre, ein Interface von der Art zu schreiben
[Java]public interface Drawable {
public void draw (Graphics g);
}[/Java]
In deinem JPanel hast du dann eine [c]List<Drawable> drawables[/c] und machst
[Java]public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable object : drawables) {
object.draw(g);
}
}[/Java]

Edit: Und wenn dann halt ne andere Klasse sich auf den Panel zeichnen will, muss die andere Klasse eben [c]panel.addDrawable(this)[/c] oder so machen
 
Heisst das, ich kann nur in ein JPanel zeichnen wenn ich dessen paintComponent Methode modifiziere? Was ist wenn ich sie nicht modifizieren kann? Kann man nicht ein graphics objekt erzeugen, in diesem graphics objekt irgendwie alle Zeichenoperationen speichern und dann einfach nur das Graphics Objekt dem Panel zu übergeben?

Mein Ziel ist es dass ein Anwender sein eigenes JPanel erzeugt. Dieses bekommt mein Programm übermittelt und zeichnet darin ein paar rechtecke oder sowas.
 
Heisst das, ich kann nur in ein JPanel zeichnen wenn ich dessen paintComponent Methode modifiziere? Was ist wenn ich sie nicht modifizieren kann? Kann man nicht ein graphics objekt erzeugen, in diesem graphics objekt irgendwie alle Zeichenoperationen speichern und dann einfach nur das Graphics Objekt dem Panel zu übergeben?
das Design von Java erfordert die Vorgehensweise, dass man paint bzw. paintComponent überschreibt, um darin zeichnen zu können.

Mein Ziel ist es dass ein Anwender sein eigenes JPanel erzeugt. Dieses bekommt mein Programm übermittelt und zeichnet darin ein paar rechtecke oder sowas.
wieso überhaupt? Die normale Vorgehensweise in so einen Fall wäre, dass der Anwender nicht von JPanel ableitet, sondern von der eigenen Klasse MyPanel, wo die benötigten Operationen bereits drin sind.

Es gibt da noch ein paar Möglichkeiten. 1) über das Icon Interface. D.h. der Anwender erstellt kein eigens JPanel, sondern ein JLabel, welches nur ein Icon anzeigt. Über das zugewiesene Icon wird dann in das JLabel gemalt. 2) über ein BufferedImage. D.h. der Anwender bekommt eine Grafik und muss selber zusehen, wie diese in seinem Programm angezeigt wird. 3) über SwingX. SwingX ist die offizielle Erweiterung von Swing, welche noch nicht mit Java ausgeliefert wird. Kann man aber problemlos bekommen und in den Classpath einfügen:

https://swingx.dev.java.net/

SwingX enthält die Komponente JXPanel, welche wiederum das Interface Painter aufnehmen kann, was eigentlich das ist, was Du mit deinem Interface "ISlot" haben willst. An dieser Stelle wollte ich auf das JavaDoc von JXPanel und Painter verweisen, aber ich finde keine aktuelle Version online.

EDIT: noch ein Vorgehen. 4) Wenn man das JPanel bekommt, darauf GridLayout(1, 1) setzen und seine eigene Komponente einfügen. Das wäre doppelt gemoppelt und das Anwender JPanel darf keine anderen Sachen enthalten. Weiterhin muss man dafür Sorgen, dass die eigene Komponente aus dem Anwender JPanel entfernt wird, wenn die Zeichenoperationen nicht mehr benötigt werden.

Slawa
 
Zuletzt bearbeitet:
das Design von Java erfordert die Vorgehensweise, dass man paint bzw. paintComponent überschreibt, um darin zeichnen zu können.

Das verwirrt mich jetzt gerade ziemlich.

Ich dem Projekt, an dem ich seit Monaten arbeite funktioniert das so Problemlos:
Java:
    public static void main(String[] args) {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        board = new JPanel();
        RootClass.game = new Engine(board, "game.ini", "game.log");
        frame.add(board);
[...]
     }

Java:
    public Engine(JPanel b, String iniFileName, String logFileName) {
[...]
        board = b;
[...]
    }

Java:
// Teil der Hauptschleife:
                    Graphics g = board.getGraphics();
                    if (g != null) {
                        renderFrame(g);
                        g.dispose();
                  }

Java:
    public void renderFrame(Graphics g) {
[...]
// backbuffer ist ein VolatileImage, auf dem Vorher die Spielszene gezeichnet wurde.
            g.drawImage(backBuffer, 0, 0, board.getWidth(), board.getHeight(), 0,
                    0, round(cordWidth * cordFactor), round(cordHeight * cordFactor),
                    null);
    }

Solange die Grafikkarte mit dem Volatileimage mitspielt hab ich so auch ein paar hundert Fps.
Keine Spur von Problemen damit ???:L
 

Je nach Spielszene teils sogar 1000+, hätte ich es nicht auf ~60 limitiert, ich weiß nicht was daran so lustig sein soll 😛

Naja, wenn das so nicht vorgesehen ist (Schande über die Websiten auf denen es so oder ähnlich vorgeschlagen wurde), dann schau ich mal das ich das ändere.

EDIT:
Java API zu getGraphics hat gesagt.:
Returns this component's graphics context, which lets you draw on a component. Use this method to get a Graphics object and then invoke operations on that object to draw on the component.

Keine Warnung nichts
Was den nun, sagt die API da falsches von sich ????
 
Zuletzt bearbeitet:
Das verwirrt mich jetzt gerade ziemlich.

Ich dem Projekt, an dem ich seit Monaten arbeite funktioniert das so Problemlos:
dieses Vorgehen ist aber für einen speziellen Zweck. Dabei wird ein eigener Thread verwendet, welcher das Neuzeichnen anstößt. In diesem Fall sollte man aber mindestens update() auf dem Panel abschalten, damit das Betriebssystem einem nicht in die Quere kommt.

Java:
// Teil der Hauptschleife:
                    Graphics g = board.getGraphics();
                    if (g != null) {
                        renderFrame(g);
                        g.dispose();
                  }
bei jedem Neuzeichnen ein neues Graphics-Objekt?

Java:
    public void renderFrame(Graphics g) {
[...]
// backbuffer ist ein VolatileImage, auf dem Vorher die Spielszene gezeichnet wurde.
            g.drawImage(backBuffer, 0, 0, board.getWidth(), board.getHeight(), 0,
                    0, round(cordWidth * cordFactor), round(cordHeight * cordFactor),
                    null);
    }
meiner Erfahrung nach ist die Skalierfunktion von Java nicht besonders performant. Deshalb sollte man besser den Backbuffer schon in der richtigen Größe haben und in renderFrame() nur 1:1 zeichnen.

Solange die Grafikkarte mit dem Volatileimage mitspielt hab ich so auch ein paar hundert Fps.
Keine Spur von Problemen damit ???:L
ich würde in so einem Fall eine bessere FPS-Messfunktion schreiben oder suchen.

Slawa
 
dieses Vorgehen ist aber für einen speziellen Zweck. Dabei wird ein eigener Thread verwendet, welcher das Neuzeichnen anstößt. In diesem Fall sollte man aber mindestens update() auf dem Panel abschalten, damit das Betriebssystem einem nicht in die Quere kommt.
Na also, das Neuzeichnen ist natürlich in einem eigenem Thread 😉
Da bin ich ja beruhigt. Was kann ich das update abschalten ?

bei jedem Neuzeichnen ein neues Graphics-Objekt?
hrhr, ja ich weiß, ist noch nicht final bzw. macht anders irgendwo Probleme.

meiner Erfahrung nach ist die Skalierfunktion von Java nicht besonders performant. Deshalb sollte man besser den Backbuffer schon in der richtigen Größe haben und in renderFrame() nur 1:1 zeichnen.
Die Fenstergröße läßt sich so während das Programm läuft jederzeit vergrößern oder verkleinern.
Sehr praktisch, imho.

Auch verwende ich die um die Qualität zu verbessern. backBuffer + Inhalt ist je nach Einstellung verschieden groß.

ich würde in so einem Fall eine bessere FPS-Messfunktion schreiben oder suchen.

Wieso sollte ein paar Hundert FPS mit dem VolatileImage und ein paar Bitmapgrafiken nicht realistisch sein 😉

Wenn ich das FPS limit auf 20 einstelle, stellt sich ein merkbares leichtes ruckeln ein, von daher wird das Meßverfahren + Limitierungsverfahren durchaus nicht total falsch liegen.
Ingametimer, die auf Basis des gleichen Zeitgebers laufen, laufen auch korrekt.
Habs mit einer Stoppuhr abgeglichen.
 

Zurück
Oben