# JScrollPane mit JPanelBMP



## SenioreFamicom (12. Jun 2008)

Hallo Leute,
erstmal mein Quellcode voran:
*Die erste Klasse:
*

```
package SN32.Editor.LowClass;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.JComponent;
import javax.swing.JPanel;


public class KBitmap extends JPanel {
    private int scaleXY = 2;
    private int width   = 480;
    private int height  = 256;
    
    public KBitmap() {
        this.setLayout(null);
        this.setSize(this.width * this.scaleXY, this.height * this.scaleXY);
        this.setLocation(0, 0);
        this.setVisible(true);
        this.repaint();
        
    }
    public void paintComponent(Graphics g) {
        Image img;
        img = getToolkit().getImage("C:/Games/Sprites/Harvest Moon/hmkuhstall.png");
        this.setPreferredSize(new Dimension(this.width * scaleXY, this.height * scaleXY));
        this.setMinimumSize  (new Dimension(this.width * scaleXY, this.height * scaleXY));
        this.setMaximumSize  (new Dimension(this.width * scaleXY, this.height * scaleXY));
        Graphics2D g2D = (Graphics2D) g;
        g2D.scale(scaleXY, scaleXY);
        g2D.drawImage(img, 0, 0, this);
    }
    public int getScaleXY() {
        return this.scaleXY;
    }
    public int getWidth() {
        return this.width;
    }
    public int getHeight() {
        return this.height;
    }
}
```

*Die zweite Klasse:*

```
package SN32.Editor.TabTile;

import SN32.Editor.Constants;
import SN32.Editor.LowClass.KBitmap;

import SN32.Editor.ReferenceBook.ReferenceBook;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;


public class TilePropReader extends JDialog implements ActionListener {
    private static final int    HINZUF  =   0;  // Button
    private static final int    SEQDEL  =   1;  // Button
    private static final int    ALLDEL  =   2;  // Button
    private static final int    ANWEND  =   3;  // Button
    private static final int    NEWSET  =   4;  // Button
    private static final int    HILFE   =   5;  // Button
    private static final int    ABBRCH  =   6;  // Button
    private static final int    RIGHT_O =   1;  // Panel
    private static final int    RIGHT_I =   2;  // Panel
    private static final int    LEFT_0L =   0;  // Panel
    private static final int    JLIST   =   0;  // ScrollPane
    private static final int    JVIEW   =   1;  // ScrollPane
    
    private File            file        =   null;   
    
    private KBitmap         bitmap      =   null;
    
    public Vector           animation   =   null;
    public JButton      []  button      =   new JButton[7];
    public JLabel       []  label       =   new JLabel[2];
    public DefaultListModel model       =   new DefaultListModel();
    public JList            list        =   new JList(model);
    public JPanel       []  panel       =   new JPanel[3];
    public JScrollPane  []  scrollPane  =   new JScrollPane[2];
    public JSpinner     []  spinner     =   new JSpinner[4];
    public JTextField       textField   =   new JTextField("()");
    
    public TilePropReader(final javax.swing.JFrame parent, final File file) {
        super(parent, true);
        
            this.file   = file;
            bitmap      = new KBitmap();
            
            // .....................................................................
            panel[LEFT_0L] = new JPanel();
            panel[RIGHT_O] = new JPanel();
            panel[RIGHT_I] = new JPanel();
            // links, inneres
            panel[LEFT_0L].setLayout(null);
            panel[LEFT_0L].setSize(480*2, 256*2);
            panel[LEFT_0L].setLocation(0, 0);
            panel[LEFT_0L].add(bitmap);
            panel[LEFT_0L].setVisible(true);
            
            //rechts, äußeres
            panel[RIGHT_O].setLayout(null);
            panel[RIGHT_O].setSize(456 + 16, 480 + 24);
            panel[RIGHT_O].setLocation(328, 16);
            panel[RIGHT_O].setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Tileset-Konfigurationen"));
            panel[RIGHT_O].setVisible(true);
            // rechts, inneres (Ani.seq.)
            panel[RIGHT_I].setLayout(null);
            panel[RIGHT_I].setSize(440, 404);
            panel[RIGHT_I].setLocation(16, 84);
            panel[RIGHT_I].setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Animationssequenzen"));
            panel[RIGHT_I].setVisible(true);
            // .....................................................................
            scrollPane[JLIST] = new JScrollPane(list);
            scrollPane[JLIST].setSize(256 + 24 - 16, 256);
            scrollPane[JLIST].setLocation(16, 56);
            scrollPane[JLIST].setBorder(BorderFactory.createEtchedBorder());
            scrollPane[JLIST].setAutoscrolls(true);
            scrollPane[JLIST].setWheelScrollingEnabled(true);
            scrollPane[JLIST].setVisible(true);
            
            scrollPane[JVIEW] = new JScrollPane(bitmap, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
            scrollPane[JVIEW].setSize(256 + 20, 480);
            scrollPane[JVIEW].setLocation(16, 16);
            scrollPane[JVIEW].getViewport().setView(bitmap);
            scrollPane[JVIEW].setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Vorschau"));
            scrollPane[JVIEW].setAutoscrolls(true);
            scrollPane[JVIEW].setWheelScrollingEnabled(true);
            scrollPane[JVIEW].setVisible(true);
            // .................................................................
            label[0] = new JLabel("Tile-Größe: ");
            label[1] = new JLabel("Transparenz-RGB: ");
            // Tile-Größe
            label[0].setSize(128, 24);
            label[0].setLocation(16, 32);
            label[0].setVisible(true);
            // Transparenz-RGB
            label[1].setSize(128, 24);
            label[1].setLocation(16, 60);
            label[1].setVisible(true);
            // .....................................................................
            spinner[0] = new JSpinner(new SpinnerNumberModel(16, 4, 64, 2));
            spinner[1] = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
            spinner[2] = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
            spinner[3] = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
            // Tile-Größe
            spinner[0].setSize(40, 24);
            spinner[0].setLocation(160, 32);
            spinner[0].setBorder(BorderFactory.createEtchedBorder());
            spinner[0].setVisible(true);
            // R
            spinner[1].setSize(40, 24);
            spinner[1].setLocation(160, 58);
            spinner[1].setBorder(BorderFactory.createEtchedBorder());
            spinner[1].setVisible(true);
            // G
            spinner[2].setSize(40, 24);
            spinner[2].setLocation(208, 58);
            spinner[2].setBorder(BorderFactory.createEtchedBorder());
            spinner[2].setVisible(true);
            // B
            spinner[3].setSize(40, 24);
            spinner[3].setLocation(256, 58);
            spinner[3].setBorder(BorderFactory.createEtchedBorder());
            spinner[3].setVisible(true);
            // .....................................................................
            button[HINZUF] = new JButton("Hinzufügen");
            button[SEQDEL] = new JButton("Seq. löschen");
            button[ALLDEL] = new JButton("Alle löschen");
            button[ANWEND] = new JButton("Anwenden");
            button[NEWSET] = new JButton("Neues Set laden");
            button[HILFE] = new JButton("Hilfe");
            button[ABBRCH] = new JButton("Abbrechen");

            button[HINZUF].setSize(128, 32);
            button[SEQDEL].setSize(128, 32);
            button[ALLDEL].setSize(128, 32);
            button[ANWEND].setSize(128, 32);
            button[NEWSET].setSize(128, 32);
            button[HILFE].setSize(128, 32);
            button[ABBRCH].setSize(128, 32);

            button[HINZUF].setLocation(296, 20);
            button[SEQDEL].setLocation(296, 68);
            button[ALLDEL].setLocation(296, 116);
            button[ANWEND].setLocation(112, 552);
            button[NEWSET].setLocation(256, 552);
            button[HILFE].setLocation(400, 552);
            button[ABBRCH].setLocation(544, 552);

            button[0].addActionListener(this);
            button[1].addActionListener(this);
            button[2].addActionListener(this);
            button[3].addActionListener(this);
            button[4].addActionListener(this);
            button[5].addActionListener(this);
            button[6].addActionListener(this);
            button[0].setVisible(true);
            button[1].setVisible(true);
            button[2].setVisible(true);
            button[3].setVisible(true);
            button[4].setVisible(true);
            button[5].setVisible(true);
            button[6].setVisible(true);
            // .....................................................................
            textField.setSize(256 + 8, 24);
            textField.setLocation(16, 24);
            textField.setVisible(true);
            // .....................................................................
            list.setSize(256 + 24 - 16, 256);
            list.setLocation(16, 56);
            list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            list.setVisible(true);
            // .....................................................................


            // ===============================
            this.add(scrollPane[JVIEW]);

            panel[RIGHT_I].add(textField);
            panel[RIGHT_I].add(scrollPane[JLIST]);
            panel[RIGHT_I].add(button[0]);
            panel[RIGHT_I].add(button[1]);
            panel[RIGHT_I].add(button[2]);

            panel[RIGHT_O].add(label[0]);
            panel[RIGHT_O].add(label[1]);
            panel[RIGHT_O].add(spinner[0]);
            panel[RIGHT_O].add(spinner[1]);
            panel[RIGHT_O].add(spinner[2]);
            panel[RIGHT_O].add(spinner[3]);
            panel[RIGHT_O].add(panel[2]);
            // ===============================
            this.setLayout(null);
            this.setSize(816, 640);
            this.setLocation(256, 192);
            this.setResizable(false);
            this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
            //this.add(panel[LEFT]);
            this.add(panel[RIGHT_O]);
            this.add(button[3]);
            this.add(button[4]);
            this.add(button[5]);
            this.add(button[6]);
            this.setVisible(true);
    }    
}
```
Das geladene Bild hat die Maße 480x256 Pixel.
Jetzt meine Frage: Wieso zeigt mein Programm beim Ausführen von der *zweiten Klasse* die Bitmap nur teilweise (genauer nur die oberen 128 Pixel) an? Erst beim Scrollen mit JScrollPane wird der Rest übermalt.


----------



## Marco13 (12. Jun 2008)

```
public void paintComponent(Graphics g) {
        Image img;
        img = getToolkit().getImage("C:/Games/Sprites/Harvest Moon/hmkuhstall.png"); 
...
```
Das Bild wird bei jedem Neuzeichnen neu geladen (und was meinst du, wie oft er neu zeichnet, wenn man scrollt!)

Du solltest das Bild EINmal im Konstruktor laden. Und dort solltest du dann einen MediaTracker http://java.sun.com/j2se/1.4.2/docs/api/java/awt/MediaTracker.html drumwickeln, um sicherzustellen, dass das Bild vollständig geladen ist, bevor es angezeigt wird.

*EDIT: Das setPreferredSize usw. sollte AUCH im Konstruktor stehen!*


EDIT2: Und wenn du das Bild auch gleich im Konstruktor skalierst, braucht das auch nicht bei jedem Zeichen neu gemacht zu werden, und es wird vmtl. *deutlich* schneller!


----------



## Marco13 (12. Jun 2008)

Kein EDIT mehr, sondern ein Nachtrag: Wenn du irgendwann mal eine deiner Components entfernen (d.h. nichtmher anzeigen) willst, wirst du mit diesen Arrays vermutlich keine Freude haben....


----------



## SenioreFamicom (12. Jun 2008)

Erstmal Danke für die schnelle Antwort 

 Okay, sind ja einige Performance-Probleme. Man merkt, dass ich Anfänger bin  :autsch: 
Aber da muss ich gleich nochmal nachfragen: Wie sieht das aus, wenn ich im Konstrukter skalieren möchte? Und was meintest du mit den Arrays? Die Komponenten in der TilePropReader?


----------



## Marco13 (12. Jun 2008)

Im Konstruktor skalieren geht am einfachsten mit Image#getScaledInstance. Für "ausgefeiltere" Sachen kannst du auch mal hier schauen: http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html


Das mit den Arrays ... naja, du hast z.B.

```
private static final int    SEQDEL  =   1;  // Button 
...
public JButton      []  button      =   new JButton[7]; 
...
button[SEQDEL] = new JButton("Seq. löschen"); 
...
panel[RIGHT_I].add(button[1]);
```
Dort wird jetzt am Ende der "Seq. Löschen" button zum Panel hinzugefügt. Wenn nun irgendwann zwischen "Hinzufügen" und "Seq. Löschen" ein weiterer Button eingefügt werden soll (oder der "Hinzufügen"-Button gelöscht werden soll) musst du
1. Die final-Konstanten anpassen (SEHR unübersichtlich!)
2. Die Array-Größen anpassen 
3. Die Indizes bei allen Zugriffen wie "button[1]" ändern - aber wie? Da ist Chaos vorprogrammiert.

Üblicherweise benennt man die Components explizit, also z.B.

private JButton addButton;
private JButton deleteButton;
....

Bei solchen Sachen wie den RGB-Spinnern kann man natürlich einen Array verwenden

private JSpinner rgbSpinners[] = new JSpinner[3];

Wenn dadurch "zu viele" Components auf einer anderen liegen, sollte man sich über eine Gruppierung Gedanken machen. Was da sinnvoll ist, muss derjenige Wissen, der das ganze schreibt. Aber vielleicht sowas wie

```
class TilePropReader
{
    private ControlPanel controlPanel;
    private ColorsPanel colorsPanel;
...
}

class ControlPanel
{
    private JButton addButton;
    private JButton deleteButton;
    ...
}

private ColorsPanel
{
    private JSpinner rgbSpinners[] = new JSpinner[3];
    ...
}
```
aber das muss man sich dann halt überlegen....


----------



## SenioreFamicom (12. Jun 2008)

Das mit getScaledInstance ist ganz nett. Also tust du von Graphics2D abraten?
Habe jetzt alles im Konstruktor eingebaut (Img laden, MediaTracker & Skalierung), trotzdem will der nicht so ganz beim Initialisieren der Image im JScrollPane auf Anhieb alles anzeigen:

So sieht es aus, wenn das Fenster geöffnet wird (der untere Teil fehlt):






Scrollt man ein Stück weiter:





Scrollt man wieder zurück, wird alles (wie es sein sollte) angezeigt:






Der neue Code:

```
package SN32.Editor.LowClass;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import javax.swing.JPanel;


public class KBitmap extends JPanel {
    private int     scaleXY = 2;
    private int     width   = 480;
    private int     height  = 256;
    private Image   img;
    
    public KBitmap() {
        img = getToolkit().getImage("C:/Games/Sprites/Harvest Moon/hmkuhstall.png");
        img = img.getScaledInstance(480*2, 256*2, 1);
        MediaTracker mTracker = new MediaTracker(this);
        mTracker.addImage(img, 0);
        
        try {
            mTracker.waitForAll();
        } catch (InterruptedException exc) {
            
        }
        setPreferredSize(new Dimension(480*2, 512));
        setMinimumSize(new Dimension(480*2, 512));
        this.setLayout(null);
        this.setSize(this.width * this.scaleXY, this.height * this.scaleXY);
        this.setLocation(0, 0);
        this.setVisible(true);
    }
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, this);
    }
   
    public int getScaleXY() {
        return this.scaleXY;
    }
    public int getWidth() {
        return this.width;
    }
    public int getHeight() {
        return this.height;
    }
}
```


----------



## SenioreFamicom (12. Jun 2008)

<hilflos>

Keine Vorschläge mehr?


----------



## Marco13 (12. Jun 2008)

Aaaaaaaaaahhhhhhh.... Jetzt hab ich bestimmt ne Stunde übelst rumprobiert, gesucht, verändert, Code von JScrollPane und JViewPost gelesen, drawModes und ClipBound gesetzt und bis zum Erbrechen debug-Ausgaben eingebaut ... um dann zu merken, dass du lügst :wink: Er zeichnet natürlich nur das, was er zeichnen muss. Und er muss nur das KBitmap zeichnen. Und das KBitmap hat eine feste Größe von 480 * 256 Pixeln. (Wenn ich das nicht die ganze Zeit in TextPad, sondern in einer IDE gemacht hätte, wäre es mir durch die farbige Hervorhebung vielleicht schon früher aufgefallen  :roll: ). Also .. du hast (absichtlich? Oder versehentlich?) die getWidth/getHeight-Methoden in der KBitmap überschrieben - daher hat er am Anfang immer zu wenig gezeichnet..... :idea:


----------



## SenioreFamicom (12. Jun 2008)

Verzeih mir xD Ich habe es mittlerweile auch bemerkt. Es war versehentlich. Mit zunehmenden Quellcode verlier ich sehr schnell die Übersicht ?___? und übersehe manchmal primtive Fehler. 
Auch ein Grund warum ich die Komponenten in Arrays mit markanten Konstanten-Name zusammenfasse. 
Übrigens, da ich die Komponenten nicht einzeln abändern, lösche, etc. werde (und wenn dann das ganze Fenster dispose), sind die Performance-Probleme nicht weiter diskutabel, oder?

Trotzdem danke für deine Bemühungen 

Das Ding hier kann geschlossen werden.


----------



## Marco13 (12. Jun 2008)

Hmja, was heißt die Übersicht: Man muss erstmal den kausalen Zusammenhang herstellen, zwischen den überschriebenen width/height-Methoden, und dem SEHR seltsamen paint-Verhalten, das damit bewirkt wurde....

Performance? Das mit den Variablennamen bezog sich auf die Übersichtlichkeit. Und das mit den Skalieren ... schon auf die Peformance ... wobei es bei einer ScrollPane, die nur ein Bild anzeigt, nicht so kritisch wäre, wie etwa später in einem Tile-based game oder so. 



			
				SenioreFamicom hat gesagt.:
			
		

> Also tust du von Graphics2D abraten?


Das hatte ich nocht nicht beantwortet: Nein, davon abzuraten macht keinen Sinn. Bestimmte Sachen gehen einfach nur (vernünftig) mit Graphics2D. In diesem speziellen Fall war aber das skalieren nicht notwendig (dass man dann auch nichtmehr auf Graphics2D casten muss, ist nur ein Kollateraleffekt...)


----------



## SenioreFamicom (12. Jun 2008)

Okay, aber noch eins:
Ich möchte jedes Tile mit einer kleinen Fußnummer versehen. Soll ich über jedes ein JLabel drüberlegen oder mit g.drawString()? Das Problem ist, dass 480*256 JLabels doch recht viel sind.


----------



## SenioreFamicom (12. Jun 2008)

PS: Außerdem soll bei Mausklick auf ein Tile eine primitive Grafik (Rechteckt) drüber gelegt werden :/


----------



## Marco13 (12. Jun 2008)

Wenn es nur um ein bißchen Text an einer bestimmten Stelle geht, solltest du auf jeden Fall g.drawString verwenden. Abgesehen davon: Von 480*256 tiles wird ja nur ein verschwindend kleiner Bruchteil gezeichnet. 

Wenn (durch skalierung) alle Tiles gleichzeitig sichtbar sind, ist das drawString zwar auch langsam, aber 1. Kann man den Text dann eh nichtmehr lesen, 2. Wäre es mit Labels noch VIEL langsamer, und 3. braucht der Text vermutlich zumindest weniger Zeit, als die Tiles selbst...


----------



## SenioreFamicom (12. Jun 2008)

Macht es denn Sinn, eine neues (temporär existierendes) Image anzulegen, in der das alte skalierte Image reinkopiert wird und ein String drübergemalt wird, sodass dieses angezeigt werden kann? Wenn ja, wie lässt sich das realisieren?
Problem ist eben auch, dass über diverse Tiles auch noch 32x32 große Images per Mausklick drübergelegt werden können. Gibt es nicht Möglichkeiten das so umzusetzen, dass der Rechner dabei nicht in die Knie geht?


----------



## Marco13 (13. Jun 2008)

Hm  :? möglich - bisher ging es um EIN Bild in einer ScrollPane - wie komplex das ganze noch werden soll (also was noch alles gemacht werden können soll) weiß man ja jetzt erstmal nicht - darum ist es auch schwer, zu sagen, WAS man WIE am günstigsten macht. Als Beispiel: Wenn man das Bild frei zoomen will (mit dem Mausrad zum Beispiel) dann würde man vermutlich NICHT getScaledInstance bei jedem Zoom-Schritt aufrufen usw. Wenn du jetzt noch einzelne Tiles ... "aufwerten" willst (mit irgendwelchen Markierungen und Text) wirst du dir sowieso über eingie Datenstrukturen Gedanken machen müssen - im Moment hast du ja EIN Bild, ohne irgendwelche Informationen, wo darin welche Tiles (und welche Markierungen) liegen. Ob diese Markierungen später dann das Bild verändern sollen, oder nur rein Informativ angezeigt werden, oder in eine "Level-Datei" oder so geschrieben werden sollen ist auch alles nicht klar, hat aber evtl. Einfluß darauf, was die günstigste Lösung wäre....

Also... Text oder ein kleines Rechteck könnte mit drawString bzw. drawRect gemacht werden. Ggf. würde das dann nicht direkt in der KBitmap-Klasse gemacht werden, sondern vielleicht in einer "Tile"-Klasse.... (wer weiß...? :wink: )

_Macht es denn Sinn, eine neues (temporär existierendes) Image anzulegen, in der das alte skalierte Image reinkopiert wird und ein String drübergemalt wird, sodass dieses angezeigt werden kann? _
Das KANN Sinn machen ... insbesondere, weil Text Zeichnen ziemlich aufwändig ist. Allerdings ist der Aufwand, sie in ein neu angelegtes Bild reinzumalen, und nachher dieses neue Bild zu malen, erstmal größer, als sie einfach nur "on the fly" von der paintComponent aus zu malen. Wenn der Text also nur einmal (oder "selten") neu gezeichnet wird, lohnt sich sowas vielleicht, ansonsten eher nicht...

_Wenn ja, wie lässt sich das realisieren?_

Eigentlich ganz einfach: 
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(theImage);
g.drawString("Hello",200,200);

Dieses BufferedImage (mit dem anderen Image und dem Text drauf) kann man dann in der paintComponent zeichnen.


----------



## SenioreFamicom (13. Jun 2008)

Okay, das klingt schonmal alles gut und hilft mir auch weiter. 
Und um mal das Verständis zu erweitern: 
- Die Nummerierung soll einmal getätigt werden. Diese müssen auch nicht verändert werden während des Programms. 
- Das Rechteck hingegen ist variabel von der Position (ein Auswahlrechteck eben).
- Genauso die kleinen Images welche über einzelne Tiles drüber gelegt werden können.
- Eine Zoom-Fkt. habe ich in dem fenster nicht geplant.
- Das Fenster dient auch nur um irgendwelche Voreinstellungen für das Tileset vorzunehmen. An sich soll nach disposen des Fensters das Tileset eh gesplittet werden und jedes Tile bekommt die zugewiesenen Eigenschaften aus dem TilePropEditor ausgehändigt.

Da fällt mir spontan ein, ob man vielleicht schon in diesem Fenster das Tileset in die einzelnen Tiles splitten soll, jedes einzelne von JComponent ableitet, die Eigenschaften jetzt schon in die Tileklasse schreibt und alle auf ein JPanel platziert oder so wie es bisher geplant war, d.h. erstmal die ganze KBitmap laden und alle 32x32 Felder bei Bedarf Eigenschaften festlegt, die beim disposen des Fensters erst den Tiles zuteilt. 
Also letztlich zielt die Frage darauf ab, ob mein Programm nicht im Schneckentempo rattert, wenn ich soviele einzelne Tiles für den Editor einführe. Nach schließen des Fenster wollte ich eh die Tiles alle seperat als JComponent, da doch zu viele Eigenschaften unterzubringen sind. Oder wäre es besser, die KBitmap überhaupt nicht zu splitten, für jede Eigenschaft ein Array mit [Bitmapbreite/32][Bitmaphöhe/32] Elemnten anzulegen? Also z.B.  

boolean [][] enableTile = new boolean[Bitmaphöhe/32][Bitmapbreite/32];

int [][] flag1 = new int[Bitmapbreite/32][Bitmaphöhe32];
int [][] flag2 = ...

usw.

Die Tiles der Bitmap werden dann im eigentlichen Editor in Abhängigkeit der Cursorposition mit seiner Breite(32, 32) einfach nur aus der Bitmap rauskopiert.

Eigentlich sollten primitive Datentypen weit weniger Speicher beanspruchen, aber nimmt es sich letztlich so viel, wenn bei der Menge alle Tiles in einzelne JComponent-Ableitungen überführt werden? Wenn nein, kannst du abschätzen, ab welchen Mengen von Tiles das kritisch werden könnte?

^^ ohje, soviele Fragen xD


----------



## SenioreFamicom (13. Jun 2008)

TableCellRenderer? ^^


----------



## Marco13 (13. Jun 2008)

Hm... also vorneweg: Ich würde NICHT Tile von JComponent erben lassen. Abgesehen davon, dass eine JComponent schon verdammt viel "unnötigen Mist" enthält (eine Auflistung hatte ich mal hier http://www.java-forum.org/de/viewtopic.php?p=401548 im zweiten Beitrag gepostet), würde ich ggf. eher eine Klasse "Tile" erstellen, die NUR das enthält, was ein Tile ausmacht. Damit wäre auch deine Frage nach den Arrays beantwortet: Man würde sicher NICHT sowas machen wie

boolean [][] enableTile = new boolean[Bitmaphöhe/32][Bitmapbreite/32];
int [][] flag1 = new int[Bitmapbreite/32][Bitmaphöhe32];
int [][] flag2 = ...

sondern eben eine Klasse

```
class Tile
{
    private boolean enabled;
    private int flag1;
    private int flag2;
    ...
}
```
und dann einen 
Tile tiles[][] = new Tile[Bitmaphöhe/32][Bitmapbreite/32];
anlegen.

Man könnte dann in den Tiles auch eine Methode anbieten wie
public void draw(Graphics g) { ... }
die das jeweilige Tile malt. WAS dort gemalt wird, bleibt dann den Tile überlassen. Es gäbe da verschiedene Strategien, z.B.

- Es gibt eine Klasse "TileMap" (wie deine KBitmap), die EIN großes Bild enthält. Die Tiles bekommen eine referenz auf dieses Bild übergeben, und "ihre" koordinaten. Sie malen dann in ihrer draw-Methode "ihren" Teil des Bildes

- Eine andere (vmtl. bessere) Möglichkeit (die IMHO recht naheliegend ist) könnte sein, den Tiles nur ihren Bildausschnitt als kleines 32x32-BufferedImage zu übergeben. Ggf. können sie dort dann auch "ihre" Zahlen reinmalen, so dass die dann sehr effizient angezeigt werden können. Die "Veränderlichen" Dinge (wie Markierungen) könnten dann in der tile.draw(...) Methode "on the fly" mit g.drawRect usw. gemalt werden.

Es gibt sicher noch 1000 andere Möglichkeiten. Welche davon am günstigsten ist, muss man sich halt überlegen.

So eine Tile-Klasse würde ich dir aber auf jeden Fall empfehlen. Die fasst einfach und praktisch alle Informationen zusammen, die für ein Tile relevant sind, und kann dann zum Beispiel(!) auch solche Methoden anbieten wie
public String createTheStringThatWillBeWrittenToTheFileForThisTile() { ... }
falls du sowas brauchst....


----------



## SenioreFamicom (14. Jun 2008)

... und tile.draw(Graphics g) in (z.B.) TileMap innerhalb der paint aufrufen? Wenn ich Sequenzen definiere, muss ich update(Graphics g) in der Klasse Tiles oder TileMap oder im Fenster implementieren?

sry, wenn ich ständig so noob-Fragen stellen muss xD An sich komme ich halbwegs klar. Aber irgendwie kann ich mich mit den paint-Angelegenheiten nicht ganz anfreunden. Vielleicht habe ich immer einen falschen Vorstellungsansatz was das Überschreiben angeht...


----------



## Marco13 (14. Jun 2008)

_.. und tile.draw(Graphics g) in (z.B.) TileMap innerhalb der paint aufrufen? _

Irgendwo von der paintComponent-Methode aus... z.B.

```
class TileMapPanel
{
     private TileMap tileMap = ...

     public void paintComponent(Grpahics g)
     {
        super.paintComponent(g);
        tileMap.draw(g);
     }
}

class TileMap
{
    private Tile tiles[][] = ...

    public void draw(Gaphics g)
    {
        for (alle tiles) tile.draw(g);
        zeichneNochMarkeirungenUndSo(g);
    }
}

class Tile
{
    public void draw(Gaphics g)
    {
         g.drawImage(...);
        zeichneNochMarkeirungenUndSo(g);
    }
}
```
(nur zur Verdeutlichung)

_Wenn ich Sequenzen definiere, muss ich update(Graphics g) in der Klasse Tiles oder TileMap oder im Fenster implementieren? _
 :bahnhof:  Sequenzen? Hm. Mit Swing hat man mit der Methode "update(Graphics g)" i.a. NICHTS mehr zu tun. Oder meintest du irgendeine eigene Methode (die dann besser NICHT update(Graphics g) heißen sollte .... versehentlich Methoden zu überschreiben, kann verheerende Auswirkungen haben :wink: )


----------



## SenioreFamicom (14. Jun 2008)

Ja, Tilesequenzen (=Tileanimationen, Hintereinanderschaltung mehrerer Tiles)  
Wenn es unkomplizierter ist, gibt es Möglichkeiten gif's zu erstellen?


----------



## Marco13 (15. Jun 2008)

Ja, falls es da eine Frage gibt, musst du sie stellen. Ob/wie man ein Animiertes GIF erstellt, kannst über websuche (oder ggf. in einem eigenen Thread) erfragen.


----------



## SenioreFamicom (15. Jun 2008)

Ok, jetzt muss ich dich nochmal heranziehen   
Ich habe das mal so umgesetzt wie von dir beschrieben, allerdings sind die Ladezeiten des JTilePropReaders bei 4 dargestellten Tiles enorm (3 - 4 Sekunden). 

Der aktuelle Code:


```
package SN32.Editor.TabTile;

import java.awt.Graphics;
import java.awt.Image;
import java.util.Vector;

class Proporties {
    public boolean      enabled     = true;
    public int          ID;
    public int          scale       = 2;
    public int          seqCounter  = 0;
    public int          tileSize    = 16;
    public int          x, y;
    public Image        img         = null;
    public Vector<Tile> sequence    = new Vector();    
}

public class Tile extends Proporties {    
    private Proporties prop = new Proporties();
    
    public Tile(Image img, int ID) {
        this.ID     = ID;
        this.img    = img;
    }
    
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    public void setLocation(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public void reset() {
        this.enabled    = true;
        this.scale      = 2;
        this.seqCounter = 0;
        this.tileSize   = 16;
        this.img        = null;
        this.sequence.clear();
    }
    public void draw(Graphics g) {
        if (img != null) {
            g.drawImage(img, x, y, null);
            g.drawString("" + ID, x, y);
        }
    }
}
```


```
package SN32.Editor.TabTile;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

public class TileMap {
    private int         cnt     = 0;
    private int         height;
    private int         width;
    private Tile [][]   tile;
    
    public TileMap(File f) {
        try {
            this.height = ImageIO.read(f).getHeight() / 16;
            this.width  = ImageIO.read(f).getWidth () / 16;
        } catch (IOException ex) {
            System.out.println("ERR");
        }
        tile = new Tile[this.width+1][this.height+1];
        BufferedImage bufImg = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
        for (int y = 0; y < this.height; y++) {
            for (int x = 0; x < this.width; x++) {
                try {
                    tile[x][y] = new Tile(ImageIO.read(f).getSubimage(x*16, y*16, 16, 16).getScaledInstance(32, 32, 0), cnt);
                    tile[x][y].setLocation(x*32, y*32);
                } catch (IOException ex) {
                    Logger.getLogger(TileMap.class.getName()).log(Level.SEVERE, null, ex);
                }
                cnt++;
            }
        }
    }
    
    public void draw(Graphics g) {
       tile[0][0].draw(g);
       tile[0][1].draw(g);
       tile[0][2].draw(g);
       tile[0][3].draw(g);
    }
}
```


```
package SN32.Editor.LowJClass;

import SN32.Editor.TabTile.*;
import java.awt.Graphics;
import java.io.File;
import javax.swing.JPanel;

public class JTileMapPanel extends JPanel {
    private TileMap tileMap = null;
    
    public JTileMapPanel(File f) {
        tileMap = new TileMap(f);
        setVisible(true);
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (tileMap != null) {
            tileMap.draw(g);
        }
    }
    
}
```


----------



## Guest (15. Jun 2008)

Hat sich erübrigt


----------



## Marco13 (15. Jun 2008)

Du hast also erkannt, dass man das ImageIO.read(f) nur EINmal machen sollte, und nicht width*height = 20000 mal :wink:


----------



## SenioreFamicom (15. Jun 2008)

Nochmals vielen Dank. Hat mir einiges geholfen was ich auch in Zukunft gut verwerten kann


----------



## SenioreFamicom (16. Jun 2008)

Okay, es lahmt trotzdem  :x verflixt. Liegt das an swing? Vielleicht räume ich den Müll nicht auf?!?: Werden denn in einer Klasse, die mit dispose geschlossen wird, auch die inneren Variablen, die ich in der Klasse einführte, aus dem Speicher geschmissen? Habe jetzt eine Mausabfrage in JTilemapPanel installiert, die ein Popup öffnet. Die Verzögerung liegt bei einer bis zwei Sekunden. Muss man mit sowas in swing leben oder sollte das eigentlich nicht so sein?  ???:L


----------



## Marco13 (16. Jun 2008)

Am besten mal alles so zusammen klatschen, dass man es einfach mit Copy&Paste hier rauskopieren und compilieren kann, dann kann man vielleicht den Grund finden.


----------



## SenioreFamicom (16. Jun 2008)

Ohje ohje. Also manche Klassen sind noch Prototypen.
Hier der Link zu den RAR-gepackten src-Folder:
http://www.file-upload.net/download-917426/src.rar.html


----------



## SenioreFamicom (16. Jun 2008)

Tileset:
http://www.file-upload.net/download-917438/farmer.bmp.html


----------



## Marco13 (16. Jun 2008)

Hm. Das System.gc sollte man i.a. nicht per Hand aufrufen. Eine 2-Sekunden-Verzögerung ..? Das Popup mit "Bearbeiten" und "Enabed" geht bei mit sofort und ohne Verzögerung auf...?! Das meintests du?


----------



## SenioreFamicom (16. Jun 2008)

Hm... Dann liegt es an meiner mieserablen Rechnerleistung. Das System.gc() habe ich nur testweise aufgerufen, um zu sehen, ob das was ändert. Gibt es sonst noch Dinge, die ich beachten soll (von dem was vorliegt), die die Perfomance erhöhen?


----------



## Marco13 (16. Jun 2008)

Hab natürlich nicht alles gelesen, aber Performancetechnisch ist mir da jetzt nichts ins Auge gesprungen. Eher strukturell (public fields, "interface inheritance" (die Constants), teilweise etwas unübersichtlich....) aber naja...


----------

