# Nonogramm Felderanordnung



## adihubba (25. Januar 2010)

Hallo,
für die Schule muss ich ein Nonogramm programmieren (ein japanisches Rätselspiel). Für die einzelnen Felder, die sich beim klick verändern, hab ich ne eigene Klasse geschrieben.
Jetzt muss man die Felder-Objekte noch in ein Jframe reinbekommen.
Allerdings sollte das Nonogramm dynamisch sein. Die Größe und Daten bekomme ich aus einer Datei, die ich einlesen muss. Und man sollte nacher auch den Spielstand speichern und zoomen können.

Jetzt stellt sich mir die Frage, was eine geeignete Vorgehensweise ist, um die Feld-Objekte anzuordnen.
Bisher hab ich zwei Ideen.
Erste: eine dynamische JTable implementieren, allerdings hab ich noch keine Erfahrung mit der Klasse Jtable und weiß nicht ob man die gut vergrößern kann.
Zweite: Das Layout vom Fenster auf GridLayout setzen und dann mit einer schleife die Anzahl an objekten einfügen. Find ich persönlich einfacher.
Hab die zweite Variante testweise probiert und dann mit ner schleife den Quelltext "jframe.add(new Feld());" ausgeführt.
Jetzt stellt sich mir aber die Frage, wie ich die einzelnen Felder anspreche, bzw. auf deren methoden zugreifen kann, da die Objekte ja keiner Variabel zugewiesen worden sind.

Jemand eine bessere Idee oder Vorschläge.
Vielen Dank schonmal


----------



## Vereth (26. Januar 2010)

Platziere die Felder nicht direkt im Frame, sondern verwende  eine eigene von JPanel abgeleitete Klasse als Container. Diese verwaltet dann die Felder in einem 2D-Array. Den Feldern gibst du eine ID, aus der du den Zeilen- und Spalten-Index berechnen kannst. Möglicherweise brauchst du noch nicht einmal das 2D-Array, sondern kannst die Felder direkt über den LayoutManager aufrufen.


----------



## adihubba (26. Januar 2010)

Hallo,
hättest du ein kleines Codebeispiel für mich, da ich deinen Vorschlag nicht ganz nachvollziehen kann. Wenn man auf die Felder durch ein einfaches 2D-Array zugreifen kann, wäre das natürlich optimal!

Die Klasse Feld erbt bei mir von Canvas. Bei der Erzeugung bekommt sie einen Wert übergeben, der die Länge und Breite des Feldes in Pixel angibt. Dann verändert sie sich halt beim Klick darauf.
Und mit den Feld-Objekten soll ich das 2D-Array füllen, oder wie?


----------



## Vereth (26. Januar 2010)

Anstatt dir ein paar wenig aussagekräftige Codefragmente zu präsentieren, beschreibe ich das Gesamtkonzept, damit dir das komplexe Zusammenspiel der einzelnen Komponenten klarer wird. Dabei werde ich eine Top-Down-Strategie verwenden, d.h. vom einfachen Überblick ausgehend deren einzelne Bestandteile zu analysieren. Dadurch wirst du (hoffentlich) lernen, wie man eine gegebene Aufgabenstellung so 'seziert', dass die Implementierung nur noch reine Fleißarbeit ist. Bei der Implementierung solltest du dann eine Bottom-Up-Strategie anwenden, d.h. erst die Klassen für die atomaren Bestandteile programmieren (bei dir sind das die Felder), dann die Klassen für deren Container usw.
Ich setze voraus, dass du Swing-Klassen verwendest. Man kann das alles auch mit AWT-Klassen machen, aber ich glaube, dass du auch Swing-spezifische GUI-Elemente verwenden möchtest, beispielsweise eine _JTable_ für die Auswahl des gewünschten Nonogramms.
Wenn du dir ein Nonogramm anschaust, wirst du sehen, dass es eine Tabelle ist, die aus drei Untertabellen besteht; je eine für die Spalten- und Zeilenköpfe, wo die Längen der Streifen angegeben sind, und eine dritte, die als Bild-Raster fungiert. Ich werde mich bei der Darstellung im wesentlichen auf das Raster beschränken, für die anderen beiden Untertabellen gilt, in leicht abgewandelter Form, die gleiche Vorgehensweise.
Für das Nonogramm definierst du eine Klasse _Zettel_, die du von _JPanel_ ableitest. Dieses Panel hat, je nach Zoomstufe, eine unveränderliche Größe. Beim Einbauen in deine GUI wird es in ein _JScrollPane_ eingebettet. Du erweiterst diese Klasse um eine _setCellSize_-Methode zur Festlegung der Zoomstufe und eine _BuildUp_-Methode, welche den ganzen internen Aufbau macht; du kannst auch einen zusätzlichen Konstruktor definieren, der diese _BuildUp_-Methode aufruft, aber das ist nicht unbedingt notwendig. Zusätzlich kannst du hier auch eine Statistik verwalten, die überprüft, ob das Raster vom Benutzer den Vorgaben entsprechend ausgefüllt wurde, aber darauf kannst du beim Prototyp deiner Anwendung erstmal verzichten.
_BuildUp_ benötigt drei Parameter: je eine 2D-Tabelle für die Daten der Zeilen- und Spaltenköpfe sowie die Größe der einzelnen Zellen. Die Tabelle für die Spaltenköpfe organisierst du nach dem Muster _ColumnHeader_[col][row], diejenige für die Zeilenköpfe nach dem Muster _RowHeader_[row][col]. Aus den Dimensionen der beiden Tabellen kannst du über die jeweiligen length-Attribute nicht nur die Anzahl der Reihen und Spalten für dein _Raster_-Objekt ermitteln, sondern auch die Anzahl der zusätzlichen Zeilen und Spalten für die Tabellen der Zeilen- und Spaltenköpfe. Jede dieser drei Untertabellen bekommt eine eigene Klasse, die du wiederum von _JPanel_ ableitest.
Für das Panel _Zettel_ erzeugst du einen _GridBagLayout_-Manager. Die Anzahl der Zeilen und Spalten ist jeweils die Anzahl der Zeilen und Spalten in deinem _Raster_-Objekt plus die Anzahl der für die Zeilen- und Spaltenköpfe zusätzlich benötigten Zeilen und Spalten. Die Objekte der einzelnen Tabellen bettest du dann in den jeweiligen Zellenbereich ein. Nun weißt du, wieviele Zeilen und Spalten du insgesamt hast. Die _BuildUp_-Methode kann nun die Methode _setCellSize_ aufrufen, der sie den Parameter für die Zellengröße übergibt.
Die Methode _setCellSize_ berechnet aus der Zellengröße sowie der Anzahl der Reihen und Spalten den Platzbedarf und passt dementsprechend die Fenstergröße an. Durch den Layout-Manager werden dadurch automatisch auch die Panels für die drei Untertabellen auf die richtige Größe skaliert.
Für die Notizen erstellst du eine Klasse _Raster_, welche du von _JPanel_ ableitest. Sie ist relativ einfach aufgebaut, weil der größte Teil der Funktionalität automatisch abläuft. Für _Raster_ verwendest du einen normalen _GridLayout_-Manager; die Anzahl der Zeilen und Spalten übergibst du als Parameter dem Konstruktor. In jede Zelle platzierst du ein eigenes Objekt deiner Klasse _Feld_. Wird die Größe des _Raster_-Panels verändert (das geschieht, wenn das _Zettel_-Panel beim Zoomen vergrößert oder verkleinert wird), passt der Layout-Manager auch automatisch die Größe der _Feld_-Objekte an; das Neuzeichnen wird dadurch auch automatisch veranlasst.
Ein _Feld_ kann zwei Zustände haben: leer oder gefüllt. Am besten leitest du die Klasse _Feld_ von der Klasse _JCheckBox_ ab. Im Konstruktor rufst du dann die geerbten Methoden _setIcon_ und _setSelectedIcon_ auf und übergibst ihnen die gewünschten Bildchen; alles andere geschieht automatisch. Wenn du Probleme bei der Anzeige hast, solltest du sie doch von _Canvas_ ableiten; dann mußt du die Verwaltung der Zustände und das Zeichnen der entsprechenden Grafik 'von Hand' programmieren. Aber das wolltest du ja sowieso schon. 

Ich hoffe, dass du dich von so viel Informationen nicht erschlagen fühlst und wünsche dir viel Erfolg beim Programmieren. Ich würde mich freuen, wenn du irgendwann das Resultat deiner Arbeit präsentieren kannst, so dass ich auch mal ein bißchen damit spielen kann.

PS: Eine große Sammlung von Nonogrammen und anderen Spielen findest du bei Otto Janko.


----------



## adihubba (27. Januar 2010)

erstmal vielen dank für deine Hilfe.
von der aufgabenstellung her muss ich das nonogramm von einer datei einlesen und daraus erstellen. mittlerweile bin ich so weit, dass ich weiß, wie viele spalten und zeilen die 3 Tabellen haben müssen.
Die 3 Tabellen wollte ich auch in einzelne Container reinpacken und dann den Container jeweils dem Jframe hinzufügen. Und das Objekt Feld hab ich auch schon mit einer Klasse implementiert, die von Canvas erbt und über die paintMethode je nach Zustand zeichnet und beim klick darauf den zustand ändert, also sich auch noch zeichnet.
Nun steh ich immer noch vor meinem Hauptproblem, dass ich nicht weiß, wie ich die vielen Felder in dem Container ansprechen soll.

Da das ja Variabel sein muss, weiß man ja nicht wie viele Objekte von Feld man erstellen muss und kann ihm keiner Variablen zuweisen wie z.b. "Feld a= new Feld()".
So wie im unten stehenden Quellcode würde man das Raster ja richtig voll bekommen und dann in einer anderen Klasse die das JFrame implementiert dann einfach frame.add(new Raster()); schreiben. Aber jetzt bin ich halt wie gesagt vor dem Problem, dass ich nicht weiß, wie ich die einzelnen Felder im Raster-Objekt ansprechen kann.

```
public class Raster extends JPanel{
	public Raster(){
		setLayout(new java.awt.GridLayout(zellen,spalten));
		for(int i=0; i<(zellen*spalten); i++){
			add(new Feld(20,0));    // 1.Parameter für die Länge und Breite des Canvas und zweiter für den Zustand.
		}
		setVisible(true);
	}
}
```

Meine andere Idee war halt, in dem Raster eine JTable einzufügen die eine feste Größe hat und dann den einzelnen Zellen eine gleiche Größe zu geben und dann über die Zeilen und Spaltenindizes zu gelangen. Aber das war bisher auch noch nicht zu erfolgreich, da die JTable anstatt das Objekt von Canvas zu Zeichnen in der Zelle, irgendeinen Text speichert. Ich vermute der Text ist einfach von der toString() methode eines jeden objekts. Kann ja mal meine ersten Versuche posten von der JTable^^

```
public void erstelleTable(){
		gameTable = new JTable(3,3);		//testweise einfach mal 3mal 3 groß
		int breit=50;	//für die Variable größe der Zellen.
		gameTable.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
		for (int i = 0; i < gameTable.getColumnCount(); i++) {
			gameTable.getColumnModel().getColumn(i).setPreferredWidth( breit );
        }
		gameTable.setRowHeight(breit);
		
		for(int i=0; i<3; i++){
			for(int j=0; j<3; j++){
				gameTable.setValueAt(new Feld(20,0), i, j);		//Sollte in jeder Zelle ein Objekt vom Typ Feld speichern
			}
		}		
		frame.add(gameTable);
	}
```
Dann steht in den einzelnen Zellen sowas komisches wie: Feld[canvas8,0,0,20x20,invalid]
Kann das sein, dass eine Jtable mit Canvas und ähnlichen Objekten gar nicht umgehen kann, sondern nur mit Zeichen?

Joa, wenn ich halt wüsste, wie ich von den Feldern die getZustand und setZustand Methode aufrufen könnte, dann sollte der Rest der Aufgabe nicht mehr so schwierig sein.

PS: Wenn mein Programm am Ende so weit läuft, kann ich es gerne nach Bewertung zur Verfügung stellen.


----------



## adihubba (27. Januar 2010)

hatte heute nacht die erleuchtung und hab 2DinA4 seiten voll mit code geschrieben^^
denke jetzt wird alles kein großes problem mehr... nur noch schreibarbeit.


----------



## XanderFlash (29. Januar 2010)

hi adihubba,

Kannst du den Code vlt mal einstellen, weil ich das Thema recht interessant finde und mir das auch gerne mal angucken würde.

mfg XanderFlash


----------



## adihubba (29. Januar 2010)

Hallo,
bin zwar so gut wie fertig mit meiner Aufgabe, allerdings muss ich noch einige Wochen warten, bevor ich den Code veröffentlichen kann.
Fallse noch interesse hast, spätestens anfang märz ist es so weit^^


----------

