# Probleme mit eigenem Canvas und ScrollPane



## zehner (26. Mrz 2004)

Hallo 
Habe ein kleines Problem mit ScrollPanes. Habe mal einen kleinen ChatClient programmiert mit verschiedenen Textfarben und Emoticons. In Swing ist das ja kein Problem. Dank Microsoft und Ihrer tollen JVM hilft mir das ja nicht viel. Also bastele ich das mal in AWT um. Mein Problem ist, dass ich so meine Schwierigkeiten mit Canvas und ScrollPanes habe. Das ganze funktioniert soweit wunderbar, dass mir die Nachrichten schön untereinander angehängt werden, aber er will nicht scrollen. Das AWT Tutorial von Sun gibt ja in dem Bereich nicht viel her, und meine AWT-Referenz von O'Reilly auch nicht unbedingt.
Wie es in der Theorie funktioniert ist mir glaube ich klar, aber ich habe so meine Probleme mit der Praxis. Ich habe schon in append() von ChatCanvas erstmal invalidate() für das Canvas aufgerufen, dann validate() für das ScrollPane und ich habe getPreferredSize() und getMinimumSize() überschrieben. Das Ergebnis war, dass er zwar gescrollt hat, aber doppelt soweit wie nötig und alle vorherigen Einträge werden gelöscht.
Ausserdem habe ich so meine Probleme mit dem Scrollen. Wenn ich mit setSize() im append() des Canvas die neue Höhe von y zuweise(die Breite sollte ja immer gleich bleiben) und mit setScrollposition(0, y) dann dahinspringe, stimmt zwar die Position, aber der gesamte Text ist weg. Ich bin schon kurz vor dem Durchdrehen, also könnte mir vielleicht jemand von euch mal erklären (am besten mit Codebeispielen), wie ich das ScrollPane so einsetze, dass ich durch mein Canvas scrollen kann und wie ich das Canvas dazu bringe, beim scrollen nicht immer den Text zu löschen. Ich weiß, dass sind gleich zwei Wünsche auf einmal, aber ich bin wirklich kurz vor dem Durchdrehen
Danke schonmal im Voraus
Gruß
zehner

```
public class ChatClient extends Applet implements Runnable {
    private Panel centerP;
    private ScrollPane chatP;
    private ChatCanvas chatC;

	...

    private void initComponents() {
	centerP = new Panel();
        chatP = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);
        chatC = new ChatCanvas();
        chatP.add(chatC);
        centerP.add(chatP);
	
	...
	
	setLayout(new BorderLayout(5, 5));
	add("Center", centerP);

	...

     public void run() {
        try {
            while(true) {
                String nachricht = inputStream.readUTF();
                chatC.append(nachricht);
            }
        }
	
	...
}

class ChatCanvas extends Canvas {
    String nachricht;
    Font nachrichtF = new Font("Dialog", Font.PLAIN, 12);
    FontMetrics fm;
    int abstand, y;
    
    public ChatCanvas() {
        fm = this.getFontMetrics(nachrichtF);
        abstand = fm.getHeight();
        y = abstand;
    }
    public void update(Graphics g) {
        setBackground(Color.WHITE);
        g.setFont(nachrichtF);
        g.drawString(nachricht, 5, y);
        y+=abstand;
    }
    public void paint(Graphics g) {
        update(g);
    }

    public void append(String input) {
        this.nachricht = input;
        Graphics g = getGraphics();
        update(g);
    }
}
```


----------



## Beni (26. Mrz 2004)

Canvas speichert das gezeichnete nicht. Nur schon wenn du ein anderes Fenster über das Canvas zieht, wird alles gelöscht...

Versuch das Ganze mal mit einer java.awt.TextArea ! (die hat die ScrollBars auch gleich mitgeliefert   )

mfg Beni


----------



## zehner (26. Mrz 2004)

Mit einer TextArea würde alles wunderbar funktionieren, aber das Problem ist, dass ich die paint Methode der Textarea nicht für verschiedene Fonts u.v.a. Images überschreiben kann. Daher ja die umständliche Lösung mit dem Canvas


----------



## Beni (26. Mrz 2004)

Sorry, hab deinen Text nicht so genau gelesen...

Andere Möglichkeit:
Du speicherst den Text irgendwo (z.B. in einem Vector). Damit du die Canvas selbst ganz neuzeichnen kannst (bei jedem Aufruf von paint alles neuzeichnen).

Und dann kannst du die setScrollposition verwenden.


----------



## zehner (26. Mrz 2004)

Das artet ja langsam in einen Chat aus, so schnell kommen hier die Antworten.
Vector fällt leider flach, soweit ich weiss ist der erst in Java 1.1.5 drin und Microsoft nimmt nur 1.1.4 abwärts.
Was ich nicht ganz verstehe ist, warum eigentlich Vectoren(oder was ähnliches um den Text zwischenzuspeichern), bisher hängt er den Text ja auch unten an. Erst wenn er zu scrollen beginnt, dann ist alles weg.
Ich versteh das einfach nicht


----------



## Beni (26. Mrz 2004)

*Werbung* hier Chat

Also: das Canvas ist eine einfache Schicht Pixel, die man mit den Graphics jederzeit beeinflussen kann. Normalerweise behält es diese Schicht Pixel (es macht einfach so wenig wir nur möglich).
Manchmal hat das Canvas aber das Gefühl, dass seine Anzeige nicht mehr simmen kann (bei Grössenänderungen z.B.), dann löscht es einfach mal alles.

Die Paint-Methode ist eigentlich dazu bestimmt, dass _alles_ neugezeichnet wird.

Deshalb solltest du alles speichern.

Wenn kein Vector da ist, schreibst du halt selber einen  :wink: (oder suchst dir sonst eine Datenstruktur die passt).

mfg Beni


----------



## zehner (26. Mrz 2004)

Danke für die Hilfe Beni
Werde es mal mit dem Zwischenspeicher machen. Was mich noch interessieren würde ist, warum es bisher ohne Vector funktioniert hat. Das Canvas hängt den Text an, und wenn ich mit System.out.println die Nachrichten und deren Position abfange, dann sehe ich auch, dass es weiterschreibt, nur eben nicht mehr im sichtbaren Bereich. Daher auch die Notwendigkeit eines ScrollPanes. Wenn ich mit repaint() ein neuzeichnen erzwinge, dann passt auch noch alles. Nur wenn das ganze auf einem ScrollPane liegt, dann funktioniert nichts.

mfg 
zehner


----------



## zehner (26. Mrz 2004)

Sollte mal jemand die gleichen Probleme haben wie ich, hier mein geänderter Code:
Es funktioniert jetzt so wie ich es haben wollte, aber da ich erst seit einem viertel Jahr in Java programmiere, bin ich natürlich für jeden Verbesserungsvorschlag dankbar.
An Beni danke nochmal, ich benutze vorläufig einen Vector bis ich sowas ähnliches selber programmieren kann
mfg
zehner

```
class ChatCanvas extends Canvas {
    private Dimension dim;
    private Vector nachrichten = new Vector(20, 10);
    Font nachrichtF = new Font("Dialog", Font.PLAIN, 12);
    FontMetrics fm;
    int abstand, y;
    
    public ChatCanvas() {
        dim = new Dimension(550, 20);
        fm = this.getFontMetrics(nachrichtF);
        abstand = fm.getHeight();
    }
    public void paint(Graphics g) {
        setBackground(Color.WHITE);
        g.setFont(nachrichtF);
        int y = abstand;
        boolean farbwechsel = true;
        for(int i=0; i<nachrichten.size(); i++) {
            if(farbwechsel)
                g.setColor(Color.BLACK);
            else
                g.setColor(Color.BLUE);
            g.drawString((String)nachrichten.elementAt(i), 5, y);
            y+=abstand;
            farbwechsel=!farbwechsel;
        }
        dim = new Dimension(550, y);
    }
    
    public Dimension getMinimumSize() {
        return getMinimumSize();
    }
    public Dimension getPreferredSize() {
        return dim;
    }
    
    public void append(String input) {
        if(nachrichten.size() > 500) {
            nachrichten.removeElementAt(0);
        }
        nachrichten.addElement(input);
        repaint();
        ScrollPane parent = (ScrollPane)getParent();
        this.invalidate();
        parent.validate();
        parent.setScrollPosition(0, this.getHeight());
    }
}
```


----------



## zehner (29. Mrz 2004)

Bräuchte doch nochmal Hilfe
Die Lösung mit dem Vector hat den Nachteil, dass ja jedesmal das gesamte Canvas neu gezeichnet wird. Wenn ich jetzt die Vectorgrösse beschränke(im Code 500), sagen wir mal auf 30, dann verschmiert erstmal jede Zeile, die nach 30 dazukommt. Wenn ich die Zeile aus dem Bild scrolle und wieder reinscrolle, dann sieht alles wieder ganz normal aus. Ein Aufruf von repaint() hilft seltsamerweise nichts. Nur wenn ich update() nicht überschreibe, dann tritt das Verschmieren nicht auf. Das muss ich aber, da ich animierte gifs in das Canvas einfüge und und die ansonsten grausam flackern. Hat vielleicht jemand einen Java 1.1 konformen Vorschlag?

Übrigens zu 1.1: Vectoren funktionieren wunderbar mit MS's JVM, nur solche Späße wie add() nicht, nimmt man aber addElement() funktionieren sie. getHeight() funktioniert nur mit Bildern, ansonsten muss man es in meinem Canvas mit getSize().height ersetzen, dann läuft es tadellos mit MS. getWidth() natürlich ebenso
Danke schonmal für die Hilfe

mfg
zehner


----------

