getSize() - Echte Höhe bestimmen

Hallöle,

ich danke für Deine Antwort und habe das noch einmal überdacht und bearbeitet.
Meine zwei Dateien haben sich noch einmal bedeutend verändert, die Version vor 2 Tagen war nur eine Zwischenversion.
Ich schicke sie gleich anbei ran.

Zu dem Static Problem: Das ist in der Tat so eine Sache. Ich habe durch meine Arbeit in Java, Swift und Python nun schon so einige wichtige Dinge kennengelernt, die ich mir heute aus dem Handgelenk schüttle, womit ich noch vor nem Monat das ganze Forum mit Fragen terrorisiert hätte. :stuck_out_tongue:
Aber bei so manchen Grundbegriffen hakt es wirklich noch, da hast Du recht. Wirklich Basics, die ich bisher nicht beachtet habe, weil sie einfach nicht relevant für mich waren. Dazu zählen static, final und abstract.

Entschuldige, wenn ich das vorerst so lasse, mich in Static und Nicht-Static einzuarbeiten ist meine nächste Angelegenheit, versprochen. :wink:
PS dazu: Ich bin in Sachen Vererbung ein wenig Faul, weil mich das mit Getter und Settern bei so vielen Variablen aufregt, habe ich generell alles protected und vererbe alle Eigenschaften weiter. Blöde Sache, die ich mir abgewöhnen werde. Zumindest letzteres. extends CafeMain an jeder Klasse hab ich mir nun abgewöhnt.
Aber um weiterhin um Getter und Setter zu kommen, bin ich, auf das hier angewiesen: import static spiel.CafeMain.*;
Wenn die von mir importierten Variablen und der Import selbst nicht static sind, dann funktioniert das ganze nicht, hat mein Informatiklehrer mir gesagt und ich stellte fest, er hat damit Recht.

Wie dem auch sei, nun zum eigentlichen Problem mit den Bildern:
Ich hatte das jetzt so umgesetzt:

    	Image tischbild = null;
    	try{
    		tischbild = ImageIO.read(new File("./src/spiel/demo_"+tische.get(tischnummer).getLand().land+".jpg"));
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	ImageObserver tischweg = null;
    	Graphics2D g2d = (Graphics2D) spielframe.getGraphics();
    	g2d.drawImage(tischbild, tische.get(tischnummer).getX(), tische.get(tischnummer).getY(), 200, 200, tischweg);
    	System.out.println(tische.get(tischnummer).getLand().land);
    }```

In dem Beispiel rufe ich dann das Zeichen eines Tisches mit `tischzeichen(nummer_des_tisches);` auf. Die dazugehörige Position ist via `setPosition` und das Pendant, `getX()` und `get()` reregelt, sodass jedes Spielelement von vornherein irgendwo festgetackert ist.

Ich danke Dir für die Idee mit dem Hintergrund, aber das ist es, wie Du herausgefunden hast nicht. Das kann ich sicherlich auch noch irgendwo verbraten, um später das Spiel graphisch knorker gestalten zu können. :)

Aber ich zeichne Einzelne Elemente und nutze die Methode dazu mehrfach.


Deine Variante sieht mir ähnlich aus wie meine, aber nicht komplett gleich. Mir wäre eine Erläuterung, welche Variante man nun aus welchem Grund bevorzugen sollte sehr genehm. :)

Vielen Dank und schönes Wochenende
Lukas


====
Die Dateien nun:
CafeMain:
```package spiel;

import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;

public class CafeMain {
	
	// System Variablen
	protected static String OS = System.getProperty("os.name").toLowerCase();
	protected static String dateipfad = new File("").getAbsolutePath();
	protected static String programmname = "Café International";
	protected static JFrame spielframe = new JFrame(programmname);
	
	// Spieler Variablen
	protected static int spieler = 1;
	protected static String[] spielername = new String[2];
	protected static int[] punktespieler = new int[2];
	protected static List<Gastkarte> kartenspieler1 = new ArrayList<Gastkarte>();
	protected static List<Gastkarte> kartenspieler2 = new ArrayList<Gastkarte>();
	
	// Spielkarten
	protected static List<Gastkarte> gastkarten = new ArrayList<Gastkarte>();
	protected static List<Laenderkarte> laenderkarten = new ArrayList<Laenderkarte>();
	
	// Spielfeld
	protected static List<Tisch> tische = new ArrayList<Tisch>(12);
	protected static List<Stuhl> stuehle = new ArrayList<Stuhl>(24);
	protected static int[] tischex = new int[12];
	protected static int[] tischey = new int[12];
	protected static int anzahlbarplaetze = 21;
	
	// Sonstiges
	protected static boolean spielernamenkorrekt = false;
	
	
	public CafeMain() throws IOException {
        spielframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        spielframe.setLocationRelativeTo(null);
        Spielfeld spielfeld = new Spielfeld();
        spielframe.add(spielfeld);
        spielframe.setSize(600,600);
        spielframe.setResizable(false);
        spielframe.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width - spielframe.getSize().width) / 2, (Toolkit.getDefaultToolkit().getScreenSize().height - spielframe.getSize().height) / 2);
        spielframe.setIconImage(Toolkit.getDefaultToolkit().getImage("./src/spiel/demo_DE.jpg"));
        spielframe.setVisible(false);
        ablauf();
	}
	
	
	
	public static void ablauf() {
		/*if(SysWin()) {
			JOptionPane.showMessageDialog(null, "Dein System ist hoffnungslos veraltet!
Windoof ist nicht kompatibel mit diesem Spiel.
Sollte es zu Problemen bei der Ausführung kommen,
dann öffne das Spiel bitte auf einem PC
mit Mac OS oder Linux!", "System veraltet", JOptionPane.WARNING_MESSAGE);
		}
		do{
		Spielstart.namensfrage();
		} while(spielernamenkorrekt == false);*/
		spielframe.setVisible(true);
		Spielfeld.zeichnen();
		Spielstart.gastkartenmischen();
        Spielstart.laenderkartenmischen();
        Spielstart.spielfeldgenerieren();
	}

	public static void main(String[] args) throws IOException {
		new CafeMain();
	}
	
	public static boolean SysWin() {
		return (OS.indexOf("win") >= 0);
	}

}

Spielfeld:


import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import static spiel.CafeMain.*;

public class Spielfeld extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        int height = getHeight();
        int width = getWidth();
        g.drawLine(0, 0, width, height);
        g.drawLine(0, height, width, 0);
    }
    
    public static void zeichnen() {
    	tischex[0] = 100; tischey[0] = 100;
    	tischex[1] = 100; tischey[1] = 200;
    	tischex[2] = 100; tischey[2] = 300;
    	tischex[3] = 100; tischey[3] = 400;
    	tischex[4] = 200; tischey[4] = 100;
    	tischex[5] = 200; tischey[5] = 200;
    	tischex[6] = 200; tischey[6] = 300;
    	tischex[7] = 200; tischey[7] = 400;
    	tischex[8] = 300; tischey[8] = 300;
    	tischex[9] = 400; tischey[9] = 300;
    	tischex[10] = 500; tischey[10] = 300;
    	tischex[11] = 550; tischey[11] = 300;
	   
		//tische.add(new Tisch()); tische.get(0).setLand(laenderkarten.get(0)); tische.get(0).setKoord(10, 10); tischzeichnen(stift,0);
		//tische.add(new Tisch()); tische.get(1).setLand(laenderkarten.get(1)); tische.get(1).setKoord(220, 10); tischzeichnen(stift,1);
		//tische.add(new Tisch()); tische.get(2).setLand(laenderkarten.get(2)); tische.get(2).setKoord(430, 10); tischzeichnen(stift,2);
	    
    }
    
    public static void tischzeichnen(int tischnummer) {
    	Image tischbild = null;
    	try{
    		tischbild = ImageIO.read(new File("./src/spiel/demo_"+tische.get(tischnummer).getLand().land+".jpg"));
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	ImageObserver tischweg = null;
    	Graphics2D g2d = (Graphics2D) spielframe.getGraphics();
    	g2d.drawImage(tischbild, tische.get(tischnummer).getX(), tische.get(tischnummer).getY(), 200, 200, tischweg);
    	System.out.println(tische.get(tischnummer).getLand().land);
    }
    
}```

Und ein paar kleinere Unbedeutende Klassen. Also Unbedeutend für das von mir hier beleuchtete Problemchen. ;)

[quote=FranzFerdinand]Ich bin in Sachen Vererbung ein wenig Faul, weil mich das mit Getter und Settern bei so vielen Variablen aufregt, habe ich generell alles protected und vererbe alle Eigenschaften weiter. Blöde Sache, die ich mir abgewöhnen werde. Zumindest letzteres. extends CafeMain an jeder Klasse hab ich mir nun abgewöhnt.
Aber um weiterhin um Getter und Setter zu kommen, bin ich, auf das hier angewiesen: import static spiel.CafeMain.*;[/quote]
Was ist an Vererbung aufwendig? Vererbung vereinfacht doch vieles? static und Vererbung haben nichts miteinander zu tun. Ohnehin ist mir hier unklar was Du hier mit Vererbung überhaupt willst? Bisher gibt es nicht wirklich etwas zu vererben.

Vergiss die Sache mit dem getGraphics() das ist der völlig falsche Ansatz damit werden die Objekte nicht dauerhaft gezeichnet bzw. auf dem Spielfeld sichtbar, einmal minimieren und wieder maximieren oder eine anderes Fenster in den Vordergrund holen und spätestens jetzt sind Deine bisher gezeichneten Tische nicht mehr zu sehen. Zeichne das komplette Spielfeld inkl. Tisch, Stühle usw… in der paintComponent.
Wie es funktioniert hat EikeB ja bereits gezeigt.

Wie gesagt, das halte ich für keine gute Entscheidung. Das ist eines der wichtigsten Konzepte in Objektorientierten Sprachen.

Das ist so nicht ganz richtig. Um auf statische Methoden/Attribute zuzugreifen kannst du folgendes schreiben: CafeMain.spielframe. Dein static import sorgt lediglich dafür, dass du CafeMain. nicht vornewegschreiben musst. Ich finde diese statischen imports in dem Zusammenhang übrigens nicht gut, weil dadurch nicht mehr klar ist wo spielframe her kommt.

Deine Variante ist falsch, meine ist besser. So einfach lässt sich das zusammenfassen :wink:
_Michael hat es ja schon erläutert. Zum einen kann es zu Fehlern kommen wenn getGraphics() null oder ein falsches Graphics Objekt zurückliefert. Außerdem verschwinden deine gezeichneten Tische wieder wenn die paintComponent Methode deiner Komponente vom System aufgerufen wird. Das passiert wenn du das Fenster verschiebst/minimierst/in der Größe änderst.

Zeichne das komplette Spielfeld inkl. Tisch, Stühle usw… in der paintComponent.

Hallöle,

das ist vielleicht möglich, ja. Aber ich füge ja im Nachhinein später auch noch andere Bilder hinzu. Wie soll ich das denn alles in dieser einen Methode machen, wenn ich diese nur einmal aufrufe, aber später nochmal was zeichnen möchte?
Das kann ich jetzt nicht nachvollziehen.

Das ist so nicht ganz richtig. Um auf statische Methoden/Attribute zuzugreifen kannst du folgendes schreiben: CafeMain.spielframe. Dein static import sorgt lediglich dafür, dass du CafeMain. nicht vornewegschreiben musst. Ich finde diese statischen imports in dem Zusammenhang übrigens nicht gut, weil dadurch nicht mehr klar ist wo spielframe her kommt.

Darum geht es doch gerade. Dass ich da nicht irgendwelchen Murks davor schreiben möchte, das verwirrt doch nur unnötig.
Ich schreibe lieber sowas hier:
tische.get(n).setKoord(tischex[n], tischey[n]);
Als mich mit sowas festzufahren:
CafeMain.tische.get(n).setKoord(CafeMain.tischex[n], CafeMain.tischey[n]);

Und das Argument nicht zu wissen, wo was herkommt ist insofern hinfällig, als dass ich sämtliche in dem Spiel verwendeten Variablen, die in mehr als einer Klasse vorkommen und nicht nur lokal in einer Methode vorkommen von vornherein in der Hauptklasse gelagert habe.

Siehe hier:

	
	// System Variablen
	protected static String OS = System.getProperty("os.name").toLowerCase();
	protected static String dateipfad = new File("").getAbsolutePath();
	protected static String programmname = "Café International";
	protected static JFrame spielframe = new JFrame(programmname);
	
	// Spieler Variablen
	protected static int spieler = 1;
	protected static String[] spielername = new String[2];
	protected static int[] punktespieler = new int[2];
	protected static List<Gastkarte> kartenspieler1 = new ArrayList<Gastkarte>();
	protected static List<Gastkarte> kartenspieler2 = new ArrayList<Gastkarte>();
	
	// Spielkarten
	protected static List<Gastkarte> gastkarten = new ArrayList<Gastkarte>();
	protected static List<Laenderkarte> laenderkarten = new ArrayList<Laenderkarte>();
	
	// Spielfeld
	protected static List<Tisch> tische = new ArrayList<Tisch>(12);
	protected static List<Stuhl> stuehle = new ArrayList<Stuhl>(24);
	protected static int[] tischex = new int[12];
	protected static int[] tischey = new int[12];
	protected static int anzahlbarplaetze = 21;
	
	// Sonstiges
	protected static boolean spielernamenkorrekt = false;
	
	
	public CafeMain() throws IOException {```

Zurück zu dem Problem mit dem Bild:
@michel_
Dass das ganze beim Minimieren oder anderes Fenster vorschieben auf einmal alles verschwindet schockt mich. Wie blöd ist das denn gelöst? Ehrlich gesagt wäre ich da nie drauf gekommen. Ich teste ja nun wirklich alle seltsamen Fehlermöglichkeiten, aber wer kommt denn darauf, dass Java zu blöd ist die Elemente die da sind beim minimieren des Fensters beizubehalten?

  @EikeB 
Ich probiere mich nun mal an Deiner Lösungsvariante:
CafeMain:
```package spiel;

import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;

public class CafeMain {
	
	// System Variablen
	protected static String OS = System.getProperty("os.name").toLowerCase();
	protected static String dateipfad = new File("").getAbsolutePath();
	protected static String programmname = "Café International";
	protected static JFrame spielframe = new JFrame(programmname);
	
	// Spieler Variablen
	protected static int spieler = 1;
	protected static String[] spielername = new String[2];
	protected static int[] punktespieler = new int[2];
	protected static List<Gastkarte> kartenspieler1 = new ArrayList<Gastkarte>();
	protected static List<Gastkarte> kartenspieler2 = new ArrayList<Gastkarte>();
	
	// Spielkarten
	protected static List<Gastkarte> gastkarten = new ArrayList<Gastkarte>();
	protected static List<Laenderkarte> laenderkarten = new ArrayList<Laenderkarte>();
	
	// Spielfeld
	protected static List<Tisch> tische = new ArrayList<Tisch>(12);
	protected static List<Stuhl> stuehle = new ArrayList<Stuhl>(24);
	protected static int[] tischex = new int[12];
	protected static int[] tischey = new int[12];
	protected static int anzahlbarplaetze = 21;
	
	// Sonstiges
	protected static boolean spielernamenkorrekt = false;
	
	
	public CafeMain() throws IOException {
        spielframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        spielframe.setLocationRelativeTo(null);
        Spielfeld spielfeld = new Spielfeld();
        spielframe.add(spielfeld);
        spielframe.setSize(600,600);
        spielframe.setResizable(false);
        spielframe.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width - spielframe.getSize().width) / 2, (Toolkit.getDefaultToolkit().getScreenSize().height - spielframe.getSize().height) / 2);
        spielframe.setIconImage(Toolkit.getDefaultToolkit().getImage("./src/spiel/demo_DE.jpg"));
        spielframe.setVisible(false);
        ablauf();
	}
	
	
	
	public static void ablauf() {
		/*if(SysWin()) {
			JOptionPane.showMessageDialog(null, "Dein System ist hoffnungslos veraltet!
Windoof ist nicht kompatibel mit diesem Spiel.
Sollte es zu Problemen bei der Ausführung kommen,
dann öffne das Spiel bitte auf einem PC
mit Mac OS oder Linux!", "System veraltet", JOptionPane.WARNING_MESSAGE);
		}
		do{
		Spielstart.namensfrage();
		} while(spielernamenkorrekt == false);*/
		Spielstart.gastkartenmischen();
        Spielstart.laenderkartenmischen();
        Spielstart.spielfeldgenerieren();
        spielframe.setVisible(true);
		Spielfeld.zeichnen();
	}

	public static void main(String[] args) throws IOException {
		new CafeMain();
	}
	
	public static boolean SysWin() {
		return (OS.indexOf("win") >= 0);
	}

}

Spielfeld:


import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import static spiel.CafeMain.*;

public class Spielfeld extends JPanel {
	
	private Image tischbild;
	@Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
 
        int height = getHeight();
        int width = getWidth();
 
        g.drawLine(0, 0, width, height);
        g.drawLine(0, height, width, 0);
 
        if (tischbild != null) {
            g.drawImage(this.tischbild, 20, 20, this.tischbild.getWidth(null), this.tischbild.getHeight(null), null);
        }
    }
    
    public static void zeichnen() {
    	tischex[0] = 100; tischey[0] = 100;
    	tischex[1] = 100; tischey[1] = 200;
    	tischex[2] = 100; tischey[2] = 300;
    	tischex[3] = 100; tischey[3] = 400;
    	tischex[4] = 200; tischey[4] = 100;
    	tischex[5] = 200; tischey[5] = 200;
    	tischex[6] = 200; tischey[6] = 300;
    	tischex[7] = 200; tischey[7] = 400;
    	tischex[8] = 300; tischey[8] = 300;
    	tischex[9] = 400; tischey[9] = 300;
    	tischex[10] = 500; tischey[10] = 300;
    	tischex[11] = 550; tischey[11] = 300;
	   
		//tische.add(new Tisch()); tische.get(0).setLand(laenderkarten.get(0)); tische.get(0).setKoord(10, 10); tischzeichnen(stift,0);
		//tische.add(new Tisch()); tische.get(1).setLand(laenderkarten.get(1)); tische.get(1).setKoord(220, 10); tischzeichnen(stift,1);
		//tische.add(new Tisch()); tische.get(2).setLand(laenderkarten.get(2)); tische.get(2).setKoord(430, 10); tischzeichnen(stift,2);
	    
    }
    
    public void tischzeichnen(int tischnummer) {
    	/*Image tischbild = null;
    	try{
    		tischbild = ImageIO.read(new File("./src/spiel/demo_"+tische.get(tischnummer).getLand().land+".jpg"));
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	ImageObserver tischweg = null;
    	Graphics2D g2d = (Graphics2D) spielframe.getGraphics();
    	g2d.drawImage(tischbild, tische.get(tischnummer).getX(), tische.get(tischnummer).getY(), 200, 200, tischweg);
    	System.out.println(tische.get(tischnummer).getLand().land);*/
    	System.out.println(this.getClass().getResourceAsStream(""));
    	try {
            this.tischbild = ImageIO.read(this.getClass().getResourceAsStream("demo_"+tische.get(tischnummer).getLand().land+".jpg"));
            repaint();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}```

Alles schön und gut, aber ich kann es leider nicht austesten.

Nun komme ich erstmals in Berührung mit Static und Nicht-Static.
Wenn ich das hier irgendwo einfüge: `Spielfeld.tischzeichnen(n);`
Dann kommt stets die Meldung:
Erstellen eines Statischen Verweises auf die Nichtstatische Methode tischzeichnen(int) ist nicht möglich.

Interessant, interessant und ich kann das nirgendwo abrufen. Ich habe mal Spaßeshalber in sämtlichen Dokumenten den Begriff `static` entfernt. Das ergibt nun schon rein logisch keinen Sinn mehr, weil die Meldung weiterhin dasteht, obgleich dieser "statische Verweis" ja gar nicht mehr existieren kann.

Kann mir das einer Erläutern?

Gruß
Lukas

Ist viel Text & Code, ich gehe auf einige der Standardpunkte mal ein, vielleicht kann noch jemand anderes spezifischer werden:

Das ist der entscheidende Punkt, auch im Hinblick auf den zweiten Absatz, den du etwas weiter unten geschrieben hattest: DU ruftst die paintComponent-Methode NICHT auf. Die paintComponent-Methode wird praktisch „vom Betriebssystem“ aufgerufen. Und zwar immer dann, wenn das Fenster neu gezeichnet werden muss. (Und es muss eben u.a. neu gezeichnet werden, wenn es kurz verdeckt war, oder minimiert und wieder maximiert wurde).

Die paintComponent-Methode beschreibt (für diese Fälle) ALLES, was gemacht werden muss, wenn diese Component neu gezeichnet wird. Und WAS gezeichnet werden soll, hängt natürlich von der aktuellen Spielsituation ab. Wenn es z.B. darum geht, irgendwelche „Tische“ zu zeichnen, wäre eine mögliche Lösung GROB sowas wie das hier:

class TablePanel extends JPanel {

    private List<Table> tablesThatArePainted = new ArrayList<Table>();

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        for (Table table : tablesThatArePainted ) {
            paintTable(g, table);
        }
    }

    private void paintTable(Graphics g, Table table) {
        ...
        g.drawRect(table.getX(), table.getY(), table.getW(), table.getH());
        ...
    }

    void addTableThatShouldBePainted(Table table) {
        tablesThatArePainted.add(table);
        repaint();
    }
}

Die letzte Zeile, repaint(), sagt dann „dem Betriebssystem“, dass es so bald wie möglich die paintComponent-Methode aufrufen sollte, so das der neue Zustand gezeichnet wird (also der Zustand mit dem neuen Tisch, den man in die Liste gelegt hat).

Das scheint im ersten Moment eine „bequeme und einfache“ Art sein, dieser Frage aus dem Weg zu gehen, aber … trotzdem ist diese Lösung falsch. Leider kann man das nur schwer kompakt begründen, ohne zu einem Rundumschlag über Sinn und Zweck objektorientierter Programmierung anzusetzen. Aber ganz kurz: Jede Klasse sollte Objekte EINES Typs beschreiben. Beim Überfliegen sehe ich da schon einigen Kandidaten:

    // Spieler Variablen
    protected static int spieler = 1;
    protected static String[] spielername = new String[2];
    protected static int[] punktespieler = new int[2];
    protected static List<Gastkarte> kartenspieler1 = new ArrayList<Gastkarte>();
    protected static List<Gastkarte> kartenspieler2 = new ArrayList<Gastkarte>();

Das KÖNNTE (!) z.B. so gelöst werden:

class Player {
    private final String name;
    private int score;
    private final List<Gastkarte> cards;

    // Konstruktor, setter und getter
    ...
}

public class CafeMain {

    // Die Spieler. Ersetzt den Block an Variablen, der vorher unter
    // Spieler Variablen
    // stand:
    private final List<Player> players;

}

Ähnlich dazu könnte es eine Klasse „Spielfeld“ (oder „Board“) geben, und in der Hauptklasse stünde dann nur noch

public class CafeMain {
    ....

    private Spielfeld spielfeld;
}

Der „statische Verweis“ ist gerade das, was dort steht: Spielfeld.tischzeichnen(n);. Das bedeutet nicht, dass irgendwo ein static zu viel ist (sondern eher, dass irgendwo eins „fehlt“, aber das nur syntaktisch). Aber dieser konkrete Fall wird ja vermutlich bald wegfallen.

Hallöle,

vielen Dank für die Informationen, ich habe mich da durchgearbeitet.
Nun stolpere ich aber über eine Sache. Ich bastle seit Tagen daran, aber bekomme das absolut nicht hin.
So sieht meine PaintMethode aus:

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        int height = getHeight();
        int width = getWidth();
        g.drawLine(0, 0, width, height);
        g.drawLine(0, height, width, 0);
        zeichnen(); //setzt die Koordinaten fest, siehe setKoord
        
        for(int n=0;n<12;n++) {
        	try {
				Image img = ImageIO.read(this.getClass().getResource("/./spiel/demo_"+tische.get(n).getLand().land+".jpg"));
				ImageIcon icon = new ImageIcon(img);
				System.out.println(this.getClass().getResource("/./spiel/demo_"+tische.get(n).getLand().land+".jpg"));
	        	tische.get(n).setKoord(tischex[n], tischey[n]);
	        	label57[n].setIcon(icon);
			} catch (IOException e) {
				e.printStackTrace();
			}
        	
        }
    }```

Ich möchte einfach nur ein 12 JLabel zeichnen, welche alle ein ImageIcon haben.

Diese habe ich in der For-Schleife drin.
Der Pfad wird mir über die Konsole ausgegeben und ist korrekt. Das Bild ist auch vorhanden.

Aber ich bekomme stets eine NullPointerException angezeigt, in der Zeile hier:
```label57[n].setIcon(icon);```

Kann mir bitte einer verraten, was da los ist und wieso das null ist?
Das Bild existiert doch und wird ordentlich verarbeitet...


Schöne Grüße
Lukas

Das hat auch nichts mit dem Bild zutun, sondern mit deinem Array label57.
Du legst zwar das Array an…

JLabel[] label57 = new JLabel[12];

… füllst es aber nirgends, so dass jeder Eintrag in dem Array null ist. Das Array könntest du bspw. so füllen:

for (int i = 0; i < label57.length; i++) {
  label57** = new JLabel();
}

Aber:
Was genau hast du mit dem JLabel eigentlich vor? Du leitest deine Klasse von JPanel ab, um in der paintComponent() Methode selbst Dinge malen zu können. Dort kannst du mit einem JLabel nichts anfangen.
Fertige Komponenten wie das JLabel nutzt man in der Regel nur dann wenn man seine Oberfläche mit einem LayoutManager zusammenbaut.

Außerdem noch ein kleiner Performance-Tipp:
Wie oben schon geschrieben wird die paintComponent-Methode unter Umständen sehr häufig aufgerufen, bspw. wenn du dein Fenster verschiebst. Das kannst du leicht mit einem Sysout in der paintComponent Methode herausfinden. Du würdest mit deinem Code dann sehr sehr häufig das selbe Bild von der Festplatte laden, was immer langsam ist. Besser wäre es hier wenn du dir die geladenen Bilder zwischenspeicherst, bspw. in einer Map.

Hallöle und Vielen Lieben Dank für Deine Antwort,

Entschuldigung wenn das teilweise ein bisschen amateurhaft herüberkommt, graphische Oberflächen sind ein Graus. Ich arbeite seit 3 Wochen daran, einfach nur Bilder anzuzeigen, es ist eine Farce für mich. :wink: Ich sollte mir mal ne Woche Auszeit von dem Zeuchs nehmen.:smiley:

Ähm, also folgendes ist Sache. Ich wollte ursprünglich Bilder anzeigen. Aber nun bin ich umgeswitcht auf JLabels mit einem Icon. (Kann man JLabel nicht auch gänzlich ohne Panel aufsetzen? :open_mouth: )

Diese JLabel sind 12 Tische und 24 Stühle, die vorhanden sind und beim Klick darauf alle irgendetwas ausführen. Das wird dann natürlich ein ActionListener. Ein einziger, nicht 36. Weshalb ich mir nen JLabelArray draus gemacht habe.

Meine Variante sah nun so aus, es funktioniert:

        JLabel label1 = new JLabel(); label57[1] = label1;
        JLabel label2 = new JLabel(); label57[2] = label2;
        JLabel label3 = new JLabel(); label57[3] = label3;
        JLabel label4 = new JLabel(); label57[4] = label4;
        JLabel label5 = new JLabel(); label57[5] = label5;
        JLabel label6 = new JLabel(); label57[6] = label6;
        JLabel label7 = new JLabel(); label57[7] = label7;
        JLabel label8 = new JLabel(); label57[8] = label8;
        JLabel label9 = new JLabel(); label57[9] = label9;
        JLabel label10 = new JLabel(); label57[10] = label10;
        JLabel label11 = new JLabel(); label57[11] = label11;```

Aber das ist aus einer Schnapsidee heraus entstanden und viel zu umständlich. Deine kleine For-Schleife gefällt mir da schon deutlich besser.

Wenn ich das alles richtig gezeichnet bekomme, dann baue ich den ActionListener dazu. Muss mich noch ein wenig dazu belesen, wie das mit nem Array geht.
Der ruft dann, je nachdem welches JLabel er identifiziert hat die zugehörige Logik auf. Die Logik ist kein Problem (mehr). Nach ein wenig sehr starken Startschwierigkeiten hab ich die nun problemlos hinbekommen und kann sie nach meinen Ideen anpassen. Nur diese ganze Grafiksache ist eine Farce für mich.


> Außerdem noch ein kleiner Performance-Tipp:
> Wie oben schon geschrieben wird die paintComponent-Methode unter Umständen sehr häufig aufgerufen, bspw. wenn du dein Fenster verschiebst. Das kannst du leicht mit einem Sysout in der paintComponent Methode herausfinden. Du würdest mit deinem Code dann sehr sehr häufig das selbe Bild von der Festplatte laden, was immer langsam ist. Besser wäre es hier wenn du dir die geladenen Bilder zwischenspeicherst, bspw. in einer Map.


Vielen Dank für diesen Hinweis. Da hast Du absolut recht, das lagere ich in eine andere Methode aus. ;)


Schöne Grüße
Lukas

Wie genau soll deine Oberfläche denn aussehen? Hat die ein festes Layout oder soll da alles dynamisch platziert werden?

Momentan mischst du beides, was nicht gut ist. Mach vllt mal ein Mockup wie die Oberfläche aussehen soll, dann kann man dir vllt ein paar Tipps geben wie man das am besten aufbaut.

Hallöle,

die Elemente auf dem Spielfeld sind allesamt an einem Festen Platz und bleiben stets dort liegen.
Jedoch wechseln sie ab und an das Bild selbst.
Also ich habe zwei Arten von Karten, Länderkarten (12 verschiedene) und Gastkarten (26 verschiedene).
Auf diesen festgelegten Elementen wird dann immer als Bild eine dieser Karten oder ein Platzhalter für frei angezeigt.
Der Beispielcode an dem ich programmiert habe, waren Tische, da kommen die Länderkarten drauf.

Um mal ein Bild zu zeigen. Die Spielfläche ist noch größer, das drumherum ist erstmal noch irrelevant. Links und Rechts kommt noch was hin.

Aber das Zentrale Spielfeld in der Mitte sieht so aus:

Die Roten 0 bis 23 sind die Stühle und die Blauen 0 bis 11 die anderen Tischelemente, die ich versuche zu erzeugen.

Wie an meinem Code zu sehen gibt es für diese Tische die Getter und Setter setKoord(x,y), getX(x) und getY(y). Diese bestimmten die Koordinaten, welche allesamt in den beiden Arrays hier abgespeichert sind:

        tischex[1] = 100; tischey[1] = 200;
        tischex[2] = 100; tischey[2] = 300;
        tischex[3] = 100; tischey[3] = 400;
        tischex[4] = 200; tischey[4] = 100;
        tischex[5] = 200; tischey[5] = 200;
        tischex[6] = 200; tischey[6] = 300;
        tischex[7] = 200; tischey[7] = 400;
        tischex[8] = 300; tischey[8] = 300;
        tischex[9] = 400; tischey[9] = 300;
        tischex[10] = 500; tischey[10] = 300;
        tischex[11] = 550; tischey[11] = 300;```
Also Beispielsweise Tisch 3 liegt bei 100/400.

Gruß
Lukas

Edit: das Bild ist etwas Älter, die Nummern 24 bzw. 12 sind durch 0 zu ersetzen. Habe damals fahrlässig von 1-24 und 1-12 gezählt. ;)

Dann würde ich das eher mit JLabels (oder eigenen Komponenten) und einen geeigneten LayoutManager (vllt GridLayout) lösen. Der Weg über eine eigene Komponente die alle Tische zeichnet ist dann vermutlich der kompliziertere.

Dazu solltest du dir dann mal folgendes anschauen: A Visual Guide to Layout Managers (The Java
Das sollte das Thema LayoutManager etwas verdeutlichen.

[QUOTE=EikeB]Dann würde ich das eher mit JLabels (oder eigenen Komponenten) und einen geeigneten LayoutManager (vllt GridLayout) lösen. Der Weg über eine eigene Komponente die alle Tische zeichnet ist dann vermutlich der kompliziertere.

Dazu solltest du dir dann mal folgendes anschauen: A Visual Guide to Layout Managers (The Java
Das sollte das Thema LayoutManager etwas verdeutlichen.[/QUOTE]

Würdest Du also dazu raten, dass ich das JPanel absäge und zu den Grundstrukturen meines JFrames zurückkehre?

*** Edit ***

Also ich hab mal die Gesamtfläche aufgezeichnet, wie es aussehen wird:

Das in der Mitte ist die Spielfläche an sich.
Oben Links in Blau ist eine Kartenablage, da kommen Ungenutzte Karten des Spielers hin. Sollte auch anklickbar sein, weil man die ja dahin klickt.
Unten Links in Grün steht die Punktzahl der Spieler. Ein einfaches JLabel wohl. In dem Fall 11 und 17 Punkte.
Oben Rechts in Lila sind die 5 Karten von dem aktuellen Spieler der dran ist. Anklickbar natürlich. Und in Gold die 2 Ziehstapel.
Und unten Rechts steht alles mögliche. Welcher Spieler dran ist, Meldungen über ungültige Züge, etc.

Jetzt hab ich das mal beschrieben und würde an der Stelle gerne fragen:

Wie empfiehlt man mir den generellen Aufbau des GridLayouts?
Nach welchem Raster sollte ich da am sinnvollsten vorgehen?

Vielen Dank und schönen Sonntag
Lukas

JPanels und co. wirst du (eventuell) weiterhin brauchen. Bspw. als Container für Buttons oder ähnliches. Nur wirst du keine eigene Komponente mehr schreiben die von JPanel erbt (wie Spielfeld), sondern mit LayoutManagern und JLabels arbeiten um dein Spielfeld darzustellen. Hier mal ein schnelles Beispiel mit dem GridLayout:

JFrame frame = new JFrame("Cafe Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setPreferredSize(new Dimension(600, 600));

Container contentPane = frame.getContentPane();
contentPane.setLayout(new GridLayout(4, 4));

// fill the contentPane
Random rand = new Random();
for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {
        JLabel label = new JLabel(i + "," + j);
        label.setBackground(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
        label.setOpaque(true);

        // add the label
        contentPane.add(label);
    }
}

frame.pack();
frame.setVisible(true);

Du würdest dann vermutlich einige Platzhalter einbauen und nur die Felder mit Inhalt füllen, die auch einen Tisch darstellen.

Hallöle,

falls Du es übersehen haben solltest, ich habe meinen Beitrag vor Deinem noch ergänzt. :wink:

Ich danke für das Codebeispiel.

Was mich an dieser Sache immer etwas verwundert ist das Ergebnis. Habe jetzt selbst ein wenig mit dem GridLayout umhergespielt und das auch so rausbekommen.

Ich krieg da kein 4 mal 4 Feld, wie erwartet raus, das sieht dann so aus:

(EDIT: Ähm, ich musste mein Panel noch entfernen, das hat offiziell noch mein Feld oben Links belegt…)

Die Unterteilung sieht ja schon mal vielversprechend aus. Jedoch frage ich mich als GridLayoutUnwissender, ich muss mich wohl weiter damit befassen, wie ich jetzt spezifische Elemente in einen bestimmten Bereich hinzufügen kann.
Also ausgehend von Element 2,2 beispielsweise ein weiteres Innenlayout erschaffen kann.

Das muss ich mir noch ansehen.

Viele Grüße
Lukas. :slight_smile:

Welche Komponente wo angezeigt wird hängt von der Reihenfolge ab wie du sie hinzugefügt hast.

Layouts kannst du beliebig verschachteln. Eine Komponente deines GridLayouts kann bspw. wieder ein GridLayout beinhalten, aber auch ein beliebig anderes.

Okay okay, das freut mich ungemein. :slight_smile: Ich werde mich damit beschäftigen.

Aber eine Frage von vorhin möchte ich noch einmal kurz aufrollen.

Guck Dir einmal mein Bild aus Beitrag 32 an, wie das Spielfeld aussehen wird.

Hat jemand eine Kluge Idee, wie man das clever ausgestalten kann?
Also jetzt nicht programmmtechnisch, sondern rein Logisch, wie ich die Aufteilung meines GridLayouts am cleversten vornehmen sollte, ohne viele Elemente zu verschwenden, aber um ein ansehnliches Ergebnis einzufahren.

Hm. Das müßte ich alles mal detaillierter nachvollziehen, aber nur kurz nebenbei: Der Gedanke, das mit einem GridLayout zu machen, erscheint etwas suspekt. Z.B. die grünen Blöcke, 11 und 17: Wenn dort etwas drinstehen soll (es also wie EIN Block wirken soll) dann bringt einem das GridLayout nichts. Man kann zwar in Kästchen 11 ein Label legen, aber der Text darin könnte immer nur so breit sein, wie EIN Block - er könnte also nicht in Block 17 reinragen.

Welche der Kästchen sollen denn wirklich “eigene” Kästchen sein? Also, z.B. die Blaue Kartenablage: Soll es 21 Felder geben, wo man Karten hinlegen kann? Oder soll das alles EIN Bereich für Karten sein? (Ich könnte mir vorstellen, dass man manche Sachen da einfacher/schöner/eleganter machen könnte, wenn man NICHT einen (Grid-)-LayoutManager verwendet, aber nochmal: Ich müßte mir erst nochmal alles hier besprochene durchsehen…)

Hallöle,

also das ganze soll folgendermaßen fungieren:
Das Spielfeld in der Mitte sind die 12 Tische und 24 Stühle.
Oben Rechts werden dem Spieler seine 5 Karten angezeigt. Diese soll man anklicken und auf die 24 Stühle ziehen können.
Die beiden Stapel da neben sind Nachziehstapel. Der eine für die 5 Karten, wenn man eben welche auf’s Feld ablegt. Und der andere zieht Elemente auf frei gewordene Tische.

Das ganze oben Links ist ein Ablageplatz. Wenn ein Spieler nichts machen kann, muss er zwingend eine Karte dorthin abwerfen.
Die werden in diesem Dreieck einfach von 1 bis 21 abgearbeitet. Also es reicht, wenn der Spiele die Karte anklickt und dann in das Dreieck, dann wird sie in das nächste der 21 Elemente reingefüllt.

Ansonsten sind Unten Links die aktuellen Punktzahlen der zwei Spieler und unten Rechts steht wer dran ist und etwaig eine Meldungsbox, für Fehlermeldungen à la: „Der Spielzug ist ungültig.“

So weit die Logik, die ich versuche umzusetzen.

Gruß
Lukas. :slight_smile:

Inwiefern hat Logik jetzt mit Layout zu tun?

GridLayout ist leider sehr, sehr, sehr einfach gestrickt. Meistens braucht man leider mehr, als nur ein Gitter mit Komponenten - irgendwelche Abweichungen gibts immer. Besonders für ein Spiel wirst du mit dem GridLayout alleine sicher nicht glücklich werden. Hast du dir schon mal die verschiedenen Layoutmanager im Wiki angeschaut? In die Layoutsache muss man sich ein bisschen einarbeiten.

Ich hab jetzt nicht alles gelesen, aber hilft dir diese Klasse vielleicht?

import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;

/**
 * Diese Klasse berechnet von einem Container (also z.B. einem JPanel) den
 * wirklich zum Bemalen zur Verfügung stehenden Bereich und stellt
 * verschiedenste Methoden zur Verfügung, diesen zu erfragen.
 *
 * @version 1.01     2013-10-30
 * @author Crian
 */

public class PaintableAreaCalculator {

    /** Breite des bemalbaren Bereichs. */
    private final int paintableWidth;

    /** Höhe des bemalbaren Bereichs. */
    private final int paintableHeight;


    /** x-Koordinate der obere, linken Ecke des bemalbaren Bereichs. */
    private final int upperLeftX;

    /** y-Koordinate der obere, linken Ecke des bemalbaren Bereichs. */
    private final int upperLeftY;

    /** Die obere, linke Ecke. */
    private final Point upperLeftPoint;


    /** x-Koordinate der unteren, rechten Ecke des bemalbaren Bereichs. */
    private final int lowerRightX;

    /** y-Koordinate der unteren, rechten Ecke des bemalbaren Bereichs. */
    private final int lowerRightY;

    /** Die untere, rechte Ecke. */
    private final Point lowerRightPoint;


    /** Bemalbarer Bereich als Rechteck. */
    private final Rectangle rectangle;

    /**
     * Konstruktor.
     *
     * Die wirklich Zeichenfläche wird ermittelt nach:
     * http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/java_swing_zeichenflaeche_ermitteln
     *
     * @param container
     *            Zu untersuchendes Gui-Element.
     */
    public PaintableAreaCalculator(Container container) {
        /* Gesamtmaße der Komponente bestimmen: */
        final int totalPanelWidth = container.getWidth();
        final int totalPanelHeight = container.getHeight();

        /* Größe des bemalbaren bereiches ermitteln: */
        final Insets insets = container.getInsets();
        paintableWidth = totalPanelWidth - insets.left - insets.right - 1;
        paintableHeight = totalPanelHeight - insets.top - insets.bottom - 1;

        /* Obere linke Ecke des bemalbaren Bereiches bestimmen: */
        upperLeftX = insets.left;
        upperLeftY = insets.top;
        upperLeftPoint = new Point(upperLeftX, upperLeftY);

        /* Untere rechte Ecke des bemalbaren Bereiches bestimmen: */
        lowerRightX = totalPanelWidth - insets.right - 1;
        lowerRightY = totalPanelHeight - insets.bottom - 1;
        lowerRightPoint = new Point(lowerRightX, lowerRightY);

        /* Rechteck bestimmen: */
        rectangle = new Rectangle(upperLeftX, upperLeftY,
                paintableWidth, paintableHeight);
    }

    /** Getter für die Breite des bemalbaren Bereichs. */
    public int getPaintableWidth() {
        return paintableWidth;
    }

    /** Getter für die Höhe des bemalbaren Bereichs. */
    public int getPaintableHeight() {
        return paintableHeight;
    }

    /**
     * Getter für die x-Koordinate der obere, linken Ecke des bemalbaren
     * Bereichs.
     */
    public int getUpperLeftX() {
        return upperLeftX;
    }

    /**
     * Getter für die y-Koordinate der obere, linken Ecke des bemalbaren
     * Bereichs.
     */
    public int getUpperLeftY() {
        return upperLeftY;
    }

    /** Getter für die obere, linke Ecke. */
    public Point getUpperLeftPoint() {
        return upperLeftPoint;
    }

    /**
     * Getter für die x-Koordinate der unteren, rechten Ecke des bemalbaren
     * Bereichs.
     */
    public int getLowerRightX() {
        return lowerRightX;
    }

    /**
     * Getter für die y-Koordinate der unteren, rechten Ecke des bemalbaren
     * Bereichs.
     */
    public int getLowerRightY() {
        return lowerRightY;
    }

    /** Getter für die untere, rechte Ecke. */
    public Point getLowerRightPoint() {
        return lowerRightPoint;
    }

    /** Getter für den bemalbaren Bereich als Rechteck. */
    public Rectangle getRectangle() {
        return rectangle;
    }

}