Probleme mit der Rückgabe einer Variable aus paintComponent()

Moinmoin!
Ich habe schon sehr viel herumprobiert, kann aber beim besten Willen nicht auf die Lösung meines Problems kommen. Hoffentlich wisst ihr da weiter:

Mein Programm bekommt einen Text übergeben, den es dann auf mehrere Seiten aufteilt. Eine Seite ist ein JPanel, annähernd in DIN-Seitengröße, also wie eine normale Buchseite. Ist der Text zu lang für eine Seite, soll daneben der Resttext auf einer zweiten Seite angezeigt werden.

Dazu wird der Text in der paintComponent()-Methode Zeichen für Zeichen durchlaufen, verschiedene Formatierungs-Tags werden ausgewertet. Ist die Index-Variable, an die der Zeilentext gezeichnet wird, größer als die Höhe der Seite, soll der verbleibende Text in einer Rückgabemethode public String getRest() zurückgegeben werden. Wenn kein Text mehr übrig ist, wird nur “-1” zurückgegeben.

Die verschiedenen Seiten werden in einer Klasse nach folgendem Schema verwaltet:
Vector v = new Vector(); v.addElement(new Seite(text)); this.add(v.lastElement()); while (v.lastElement().getRest().compareTo("-1")!=0) { v.add(new Seite(v.lastElement().getRest())); this.add(v.lastElement()); }Problem: Obwohl der Text nicht auf die erste Seite passt, liefert diese erste Seite bei getRest() “-1” als Rückgabewert. Diese Methode gibt eine globale Variable “resttxt” aus, die in der paintComponent()-Methode geändert wird, sobald die Seitenhöhe aufgebraucht ist.

Lange Erklärung, ich hoffe es ist halbwegs klargeworden was ich meine. Über Hilfe wäre ich sehr dankbar, momentan stehe ich echt auf dem Schlauch! Danke schonmal!

Beim Code ist wohl irgendwas schiefgegangen, hier nochmal in formatiert:


 v.addElement(new Seite(text));
 this.add(v.lastElement());  
 while (v.lastElement().getRest().compareTo("-1")!=0) {
  v.add(new Seite(v.lastElement().getRest()));
  this.add(v.lastElement());
 }```

Hm. Klingt komisch. In der paintComponent sollte nicht rumgerechnet oder irgendein String zerlegt werden. Die wird ja u.U. SEHR oft aufgerufen (und man weiß nicht, wann es aufgerufen wird). Dort sollte man NUR zeichnen. Es wäre besser, den String vorher (d.h. gleich am Anfang) so aufzuteilen, dass man weiß, was in welcher paintComponent-Methode gezeichnet werden muss. Poste vielleicht mal ein bißchen mehr Code, oder beschreib’ genauer, was da am Ende rauskommen soll.

Heyho!
Danke für deine Antwort!

Also am Ende soll, auf mehrere Seiten verteilt, ein Liedtext mit Akkorden drüber angezeigt werden. Im Quelltext stehen die Akkorde in einem Tag genau an der Stelle, über der sie dann später stehen müssen. Separat einstellbar sollen die Schriftgrößen von Akkorden und Text sein. Das Problem, weswegen ich auf die eher umständliche Variante mit paintComponent() zurückgegriffen habe, war, dass der Text automatisch, wenn die Seiten verkleinert werden, einen Zeilenumbruch machen soll. Allerdings müssen auch dann die Akkorde genau über dem Text stehen,also immer abwechselnd eine Zeile Akkorde eine Zeile Text.

Alles in die paintComponent()-Methode zu packen war dabei auch nur die Übergangslösung, aber eigentlich finde ich den Weg über g.drawString gar nicht so toll.
Meine Idee jetzt: Den Text vorher Wort für Wort in eine Liste schreiben, und dazu die Position auf der X-Achse mit anzugeben. Die Akkorde würde ich einfach als Liste an das Wort-Element anhängen, und auch jeweils mit ihrer Position versehen. Welche Art von Textkomponente würdet ihr mir empfehlen? Ich habe ein wenig gesucht und bin auf das JTextPane gestoßen, für das man scheinbar ein eigenes EditorKit definieren kann. Kann ein solches EditorKit Akkorde und Text zufällig schon genauso formatieren wie gewünscht? Irgendwie gibts da nicht so viel Beispielmaterial für.

Ein eigenes EditorKit wäre wohl ziemlich aufwändig. Ich hatte mir das erst kürzlich mal ganz oberflächlich angesehen (im Zusammenhang mit http://forum.byte-welt.de/showthread.php?t=3522 ) aber es scheint, als wären “eigene EditorKits” nichts, was ein “normalsterblicher Java-Programmierer” mal kurz hinschreibt. (Was dich aber nicht abhalten soll, natürlich kann es jeder versuchen…).

Ich würde vermutlich eher den Weg über ein pragmatisches Zeichnen des Textes mit g.drawString gehen. Natürlich braucht man da einiges an Information, eben WO der Text (ggf. in welcher Schriftart) gezeichnet werden soll. Diese “Akkorde” sind doch vermutlich irgendwelche Grafischen Elemente (Notenlinien oder so?). Wenn man den Text daran ausrichten soll, müßte das doch machbar sein. Poste vielleicht mal ein bißchen Code, an dem deutlicher wird, was da abläuft (d.h. was wann wo hin gezeichnet wird, und welche Informationen man über das zu zeichnende so hat)

Kurze musiktheoretische Einführung: :wink:
Ein Akkord ist im Prinzip nur eine Buchstabenkombination, wie „Cis m“, „Bb“ oder „C“.
Eine Textzeile sähe dann im Prinzip so aus:
Hm …G
I went everywhere for you
Der Code dafür wäre in meinem dafür ausgedachten Format „I{chord Hm} went every{chord G}where for you“.

Bei der Anzeige bin ich auch mittlerweile so weit, dass alles gut funktioniert. Bis halt auf die Tatsache, dass der restliche Text nicht richtig zurückgegeben wird. Also der Code ist schon ein wenig lang, aber grundsätzlich kann man sagen, wird der Text Zeichen für Zeichen in einer for-Schleife durchlaufen (Im Moment noch in der paintComponent()).
Mittels einer if-Abfrage wird dann folgendermaßen abgebrochen, falls kein Platz mehr auf der Seite ist:

if (hindex > height - 10) {
                        resttxt = zw + txt.substring(x);
                        break;
                    }

Die Variable resttxt wurde vorher im Konstruktor mit dem Standardwert „-1“ belegt, falls halt nicht abgebrochen werden muss und kein Resttext existiert.

Das (für mich) Unerklärliche: Lasse ich hier per System.out.println die globale Variable resttxt ausgeben(, vor dem break natürlich), wird genau der richtige Resttext angezeigt. Greife ich nun aus einer anderen Methode, beispielsweise der getter-Methode auf die Variable zu:

public String getRest() {
        return resttxt;
    }

Dann hat resttxt trotzdem noch den Wert „-1“! Gerade hab ichs noch mal nachgeprüft: Die resttxt-Variable wird nicht in der paintComponent() neu erzeugt, es wird also auf jeden Fall mit der globalen Variable hantiert.

Irgendwelche klugen Ideen dazu? :smiley:

Die nahe liegende Erklärung wäre, dass die get-Methode aufgerufen wird, bevor das erste mal die paintComponent aufgerufen wird. (Das könnte man in gewissen Grenzen durch System.out.println’s rausfinden, aber das ist fummelig). Und nochmal: Wann und wie oft paintComponent aufgerufen wird, weiß man nicht!

Was ich meinte war, dass wenn du z.B. eine Eingabe hast wie
“I{chord Hm} went every{chord G}where for you”.
die zerlegt werden sollte in die Informationen, die in der paintComponent dann NUR noch gelesen werden, um zu zeichnen (und die nicht mehr verändert werden). Das könnte dann (ganz grob im halb-Pseudocode anskizziert) sowas sein wie

List<PaintInfo> compute(String input)
{
    List<PaintInfo> paintInfos = new ArrayList<PaintInfo>();
    while (there is remaining input)
    {
         String pageContent = readContentForOnePage(input);
         String text = readTextFrom(pageContent);
         String accords = readAccordsFrom(pageContent);
         paintInfos.add(new PaintInfo(text, accords));
    }
    return paintInfos;
}


class SinglePage extends JPanel
{
    private PaintInfo paintInfo; 

    public void paintComponent(Graphics g)
    {
        drawStuff(paintInfo); 
    }
}

Also ausrechnen, was auf jeweisl eine Seite passt, und diese “Objekte” dann zeichnen.

Aber vermutlich wäre es besser, wenn du noch etwas mehr code posten würdest. So ist das nur ziemlich grundlagenarmes Rumgerate…

Ich bin gerade dabei die Seitenklasse umzuschreiben, dazu eine kurze Zwischenfrage:
Gibt es irgendeine Möglichkeit, an die Länge eines Wortes zu kommen, also an ein metrics-Objekt, bevor die Seite erzeugt wurde (Also ohne deren Graphics g zu kennen)?

Ich habe jetzt folgende Klasse “Chordstruct” geschrieben, die den String vorher schon so aufteilt, dass er eigentlich sehr komfortabel zu zeichnen wäre, wenn die metrics irgendwie schon bestimmbar wären:

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.font.TextLayout;
import java.util.Vector;
import org.w3c.dom.Node;

public class Liedstruct {

protected Node txt;
protected String modus;
protected Vector<zeile> zeilen;

int Chordsize, Headsize, Fontsize, chordHeight, headHeight, textHeight,
		rand, hindex;
Font Chordfont, Headfont, Textfont;
FontMetrics chordmetrics, headmetrics, textmetrics;
String Schriftart;
Graphics2D gd;
TextLayout Tl;

public Liedstruct(Node t, String m, Graphics2D g) {
	txt = t;
	modus = m;
	gd = g;

	updateFonts();

	Tl.getOutline(null).getBounds();

	zeilen = new Vector<zeile>();
	setZeilen();
}

protected void updateFonts() {
	
	Schriftart = MainProgramm.Schriftart;

	Chordsize = Integer.parseInt(MainProgramm.Chordsize);
	Headsize = Integer.parseInt(MainProgramm.HeadSize);
	Fontsize = Integer.parseInt(MainProgramm.Fontsize);

	Chordfont = new Font(Schriftart, Font.PLAIN, Chordsize);
	Headfont = new Font(Schriftart, Font.ITALIC, Headsize);
	Textfont = new Font(Schriftart, Font.PLAIN, Fontsize);

	gd.setFont(Chordfont);
	chordmetrics = gd.getFontMetrics();
	chordHeight = chordmetrics.getMaxAscent();

	gd.setFont(Headfont);
	headmetrics = gd.getFontMetrics();
	headHeight = headmetrics.getMaxAscent();

	gd.setFont(Textfont);
	textmetrics = gd.getFontMetrics();
	textHeight = textmetrics.getMaxAscent();

	rand = 5;
}

private void setZeilen() {
	for (int i = 0; i < txt.getChildNodes().getLength(); i++) {
		if (txt.getChildNodes().item(i).getNodeName().compareTo("struct") == 0) {
			// Ueberschrift der Struktur, z.b. Strophe, Chorus, ...
			zeilen.add(new zeile(txt.getChildNodes().item(i)
					.getAttributes().getNamedItem("name").getNodeValue(),
					"Ueberschrift"));
			for (int y = 0; y < txt.getChildNodes().item(i).getChildNodes()
					.getLength(); y++) {
				if (txt.getChildNodes().item(i).getChildNodes().item(y)
						.getNodeName().compareTo("ln") == 0) {
					// Strukturinhalt
					zeilen.add(new zeile(txt.getChildNodes().item(i)
							.getChildNodes().item(y).getNodeValue(),
							"TextChord"));
				}
			}
		}
	}
}

class zeile {

	private Vector<wort> woerter;
	private String typ, text;
	private int gesamtlaenge, hoehe;

	protected zeile(String tx, String ty) {
		typ = ty;
		text = tx;
		gesamtlaenge = 0;
		hoehe = 0;
		if (typ.compareTo("Ueberschrift") != 0) {
			hoehe = chordHeight + textHeight + (chordHeight / 2);
			buildWords();
		}else{
			hoehe = headHeight + (int)(0.5 * headHeight);
		}
	}

	private void buildWords() {

		boolean tagon = false;
		boolean textwrite = false;
		String worttext = "";

		for (int x = 0; x < text.length(); x++) {
			if (text.charAt(x) == '{') {
				tagon = true;
			} else if (text.charAt(x) == '}') {
				tagon = false;
			} else if (!tagon && text.charAt(x) == ' ' && textwrite) {
				//Absoluten Index feststellen:
				woerter.add(new wort(worttext, gesamtlaenge));
				gesamtlaenge = gesamtlaenge + woerter.lastElement().getLaenge();
				textwrite = false;
				worttext = "" + text.charAt(x);
			} else if(!textwrite){
				worttext = worttext + text.charAt(x);
			} else {
				worttext = worttext + text.charAt(x);
				textwrite = true;
			}
			if(x == text.length()-1 && !textwrite){
				woerter.add(new wort(worttext, gesamtlaenge));
				worttext = "" + text.charAt(x);
			}
		}
	}

	protected int getLaenge() {
		return gesamtlaenge;
	}

	protected int getHoehe() {
		return hoehe;
	}

	protected Vector<wort> getZeile() {
		return woerter;
	}

	protected String getPlainText() {
		return text;
	}

	protected String getTyp() {
		return typ;
	}
}

/**
 * Ein Wort besteht aus dem Worttext, dessen Position und den Akkorden mit
 * ihren Positionen, relativ zum Wortanfang.
 * 
 * @author Sebi
 * 
 */
class wort {
	private String text;
	private String worttext;
	private Vector<chord> chords;
	private int index, laenge;

	protected wort(String t, int i) {
		text = t;
		chords = new Vector<chord>();
		worttext = "";
		index = i;
		laenge = 0;
		
		buildWort();
		
	}

	private void buildWort() {

		boolean chordon = false;
		String chordtext = "";

		for (int x = 0; x < text.length(); x++) {
			if (text.charAt(x) == '{') {
				chordon = true;
			} else if (text.charAt(x) == '}') {
				chordon = false;
				//Relativen Index feststellen:
				int chordindex = (textmetrics.stringWidth(worttext))-((int)(0.5*chordmetrics.stringWidth(chordtext)));
				chords.add(new chord(chordtext, chordindex));
				chordtext = "";
			} else if (chordon) {
				chordtext = chordtext + text.charAt(x);
			} else {
				worttext = worttext + text.charAt(x);
			}
		}
		
		laenge = textmetrics.stringWidth(worttext);
		if(laenge<(chords.lastElement().getIndex()+(int)(0.5*chordmetrics.stringWidth(chords.lastElement().getChord())))){
			laenge = (chords.lastElement().getIndex()+(int)(0.5*chordmetrics.stringWidth(chords.lastElement().getChord())));
		}
	}

	protected int getLaenge(){
		return laenge;
	}
	
	protected Vector<chord> getChords() {
		return chords;
	}

	protected int getIndex() {
		return index;
	}

	protected String getWorttext() {
		return worttext;
	}

	class chord {
		private int index;
		private String chord;

		protected chord(String c, int i) {
			chord = c;
			index = i;
		}

		protected int getIndex() {
			return index;
		}

		protected String getChord() {
			return chord;
		}
	}

}

}

Man kann höchstens mit
http://download.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#getFontMetrics(java.awt.Font)
arbeiten, sofern man schon eine Component zur Verfügung hat.

Kleinigkeiten:

  • Klassennamen Groß schreiben (Zeile, Wort)
  • Statt Vector lieber List verwenden, ist allgemeiner
// WEG: private Vector<wort> woerter;
// HIN:  
private List<Wort> woerter = new ArrayList<Wort>();
  • Die “setZeilen” Methode ist IMHO recht unübersichtlich… Ich würde eher sowas schreiben wie
private void setZeilen() {
    NodeList children = txt.getChildNodes();
    for (int i = 0; i < nodeList.getLength(); i++) {
        Node child = nodeList.item(i);
        if (child.getNodeName().equals("struct")) {
            // Ueberschrift der Struktur, z.b. Strophe, Chorus, ...
            String name = child.getAttributes().getNamedItem("name").getNodeValue();
            zeilen.add(new zeile(name, "Ueberschrift"));
            
            NodeList grandChildren = child.getChildNodes();
            for (int y = 0; y < .getLength(); y++) {
                Node grandChild = grandChildren.item(y);
                if (grandChild.getNodeName().equals("ln")) {
                    // Strukturinhalt
                    zeilen.add(new zeile(grandChild.getNodeValue(), "TextChord"));
                }
            }
        }
    }
}

aber das ist vielleicht eher eine Stilfrage.

Heyho!
Erstmal: Vielen Dank, Marco13, deine Hilfe hat mich echt einen Schritt nach vorne gebracht!
Funktionieren tut es allerdings immer noch nicht so ganz:
Ich habe jetzt das “Liedstruct”-Objekt, mit kleineren Verbesserungen statisch in die Seitenklasse eingebaut. Erweitert habe ich den Code mit

		if(s==1){
			seitenzeilen.clear();
		}
		seitenzeilen.add(s-1, (Integer)z);
	}```
Im seitenzeilen-Vektor soll der index (z) der letzten gezeichneten Zeile auf der Seite (s) gespeichert werden.
In der paintComponent() meiner Seitenklasse wird dazu nach folgendem Schema abgefragt:
```if (Ls.seitenzeilen.size() == seite) {
			int hindex = 0;
			for (int z = 0; z < Ls.getZeilen().size(); z++) {
				if (seite == 1 || Ls.seitenzeilen.elementAt(seite - 2) <= z) {
                                         //Ausgabe der einzelnen Zeilen
				}
			}
}```
Somit soll die Seite also erst gezeichnet werden, nachdem die vorherige Seite den Index ihrer letzen Textzeile angegeben hat. Leider werden im Moment mit dieser Erweiterung nur noch leere Seiten erzeugt. Lasse ich diese neue Idee weg, werden fehlerfreie Seiten erzeugt, die allerdings netürlich alle der ersten Seite entsprechen (Dazu ein Bild im Anhang). Kannst du/Könnt ihr mir vielleicht da noch einen Tipp zu geben? Muss ich vielleicht mit Threads arbeiten, um die Seitenerzeugung nacheinander richtig regeln zu können?

WIe schon seit dem Anfang des Threads: Mir ist nicht klar, was du da in der paintComponents machst oder erreichen willst. Dort Abfragen zu machen, die auf irgendwelchen sich ständig ändernden Zuständen basieren, oder Variablen zu ändern macht einfach keinen Sinn.

EDIT: Kann man sich das irgendwo mal komplett am Stück ansehen?

Ok, vielen Dank noch einmal, ich habs jetzt endlich so hinbekommen wie ich es haben wollte!

Meine Lösung:
Die einzelnen Seiten sind keine JPanels mehr, sondern werden alle in einem JPanel gezeichnet. So muss ich erst gar nicht Informationen, die erst beim Zeichnen bekannt sind, weitergeben. Einfache Lösung, hätte man auch früher drauf kommen können:D

[QUOTE=;][/QUOTE]
Coffee Uggs Classic Cardy [uug_65] - $118.99 : Ugg Boots Sale!, Ugg Boots Sale,Ugg Outlet,Ugg Store,Ugg Austrslia,Ugg Boots Clearance