JLayeredPane wird nur 1x bezeichnet

Hallo,
ich habe ein Problem mit einem JLayeredPane:
In das JLayeredPane soll nach Button click was gezeichnet werden, das funktioniert auch schon ganz gut.

Das Problem: Clicke ich den Button erneut, erscheint keine neue Grafik im JLayeredPane. Hier mal mein Quelltext:

Hier wird das Bild geladen und gezeichnet:

import java.awt.*;
import javax.swing.*;

class Zeichner extends JLayeredPane { 
	private static int x=0;
	private Image img;
	public Zeichner(){
		setSize(498,174);
		
		img = getToolkit().getImage("x2.jpg");
		
		MediaTracker mt = new MediaTracker(this);
		mt.addImage(img,0);
		try{
			mt.waitForAll();
		}
		catch(InterruptedException e){
		 System.out.println(e);	
		}
		repaint();
		
	}
	
	public void paint(Graphics g){
		
		if(img !=null){
		
		g.drawImage(img,2,2,496,172,this);
	  }
	}
}

Und so baue ich es dann ein:

class Klasse{
Zeichner malen
...
malen = new Zeichner();
GUIAnwendung.layeredPane1.add(malen);
...
}

Hat zufällig jemand eine Idee an was das liegen könnte, bin am verzweifeln???

4 Fragen:

Entfernst du das alte Pane gar nicht?

Warum benutzt du überhaupt JLayeredPane wenn du keine Funktionalität davon brauchst?

Währe es nicht geschickter das Bild was gezeichnet wird auszutauschen anstelle vom instanzieren einer neuen Komponente?

Nach deinem Quelltext lädst du doch auch immer nur die selbe Grafik?

  • Ich entferne das alte Pane nicht. Bin mir nicht sicher wie das funktioniert, hab es mal mit GUIAnwendung.layeredPane1.remove(malen); probiert, hat aber nichts gebracht.

  • JLayeredPane benutze ich eigentlich nur weil diese Variante im Vergleich zu JLabel und Panel am besten funktioniert hat. Was wäre die beste Wahl?

  • Das hört sich nicht schlecht an. Nur bin ich mir nicht ganz im klaren wie ich das machen soll.

  • Ist nicht die “selbe” Grafik. Hier werden auch noch Messwerte gesammelt, in einen Array geschrieben, 8000 Werte, ein Bild gezeichnet 8000x400 Pixel und dann eben in class Zeichner geladen und entsprechend skaliert. Habe es auch schon mit unterschiedlichen Dateinamen versucht, wird keine neue Grafik/Bild in das JLayeredPane gezeichnet.

Kann jetzt nicht nachvollziehen was so kompliziert an z.b. dem JLabel sein soll?

Hier mal ein kleines Beispiel:

    if (args.length==0) System.exit(1);
    
    JFrame frame = new JFrame("ImageTest");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLayout(new BorderLayout());
    frame.setSize(400, 300);

    final JLabel label = new JLabel(new ImageIcon(args[0]));
    frame.getContentPane().add(label, BorderLayout.CENTER);
    
    JButton button = new JButton("Nächstes Bild");
    frame.getContentPane().add(button, BorderLayout.SOUTH);
    button.addActionListener(new ActionListener(){
      private int index;
      
      public void actionPerformed(ActionEvent e) {
        label.setIcon(new ImageIcon(args[++index%args.length]));
      }
    });

    frame.setVisible(true);
  }

Also das Problem ist folgendes:
Die class GUIAnwendung ist ein JFrame, hier sind viele JLabels vorhanden. Auf eines, ich hab halt ein JLayeredPane verwendet, muß das Bild gezeichnet werden.

Das nächste ist ich kann das Bild nicht einfach mit ImageIcon laden, da es zu groß ist, sprich es muß skaliert werden.

Ok, ich habe das ganze jetzt noch mal über JLabel probiert, aber wie gesagt, das Ergebnis ist noch schlechter:

  1. Das Bild wird erst sichtbar wenn ich in der Taskleiste auf eine andere Anwendung clicke und dann wieder in meine GUIAnwendung gehe.
  2. Ein neues Bild wird auch hier nicht gezeichnet, sprich das alte bleibt erhalten.

Hier der Source:

public class GUIAnwendung extends JFrame{
...
JLabel label1 = new JLabel();
...
private void button_MessenActionPerformed(ActionEvent e) {
new andereKlasse();
...
}

Hier wird die Grafik gezeichnet und skaliert:

import java.awt.*;
import javax.swing.*;

class Zeichner extends JLabel { 
	private static int x=0;
	private Image img;
	public Zeichner(){
		setSize(498,174);
		
		img = getToolkit().getImage("x2.jpg");
		
		MediaTracker mt = new MediaTracker(this);
		mt.addImage(img,0);
		System.out.println("Bild wird geladen............");
		try{
			mt.waitForAll();
		}
		catch(InterruptedException e){
		 System.out.println(e);	
		}
		repaint();
		
	}
	
	public void paint(Graphics g){
		
		if(img !=null){
		
		g.drawImage(img,2,2,496,172,this);
	  }
	}
}

Und hier noch die andereKlasse:

class andereKlasse{
...
malen=new Zeichner();
GUIAnwedung.label1.add(malen);
...

Mahlzeit,

[Zitat:
Das nächste ist ich kann das Bild nicht einfach mit ImageIcon laden, da es zu groß ist, sprich es muß skaliert werden.

Wieso das denn ? Meinerseits kenne ich keine grössen beschränkung für ein Icon! Wenn du speicher probleme bekommst ist das eine andere sache.

Weiterhin sehe ich immernoch keinen grund hier ein JLayeredPane zu nutzen, gerade wo (sofern du java 5 benutzt) ein einfaches JPanel dir wenn du es wünscht die selbe möglichkeit bietet um komponenten anhand einer Z-Achse zu sortieren.

Weiterhin benutzt bitte nie mehe diesen alten MediaTracker, der hat sowas von ausgedient du glaubst es kaum. Zumal heutzutage die alternative klasse ImageIO schneller ist, und du sicher sein kannst das dein Bild korrekt/komplett geladen ist!

Weiterhin bekomme ich Augenkrebs wenn du über acht instantzen hinweg was anstösst und dich dann wunderst das e nicht klappt:

Meineklasse.bla.blubb.blubber.fummel.tool.ganztoll.supertoll.nochtoller.repaint();

Ich glaube kaum das das irgendwo in büchern so zu finden ist ?

So, und nun zum eigendlichen problem:

Bei AWT Komponenten überschreibt man die methode paint(Graphics g),
in Swing sieht das ganze anders aus: public void paintComponent(Graphics g)!

Und jetzt schau dir mal an was du machst :

public void paint(Graphics g){

       

        if(img !=null){

       

        g.drawImage(img,2,2,496,172,this);

      }

    }

Da das ganze in einem JLabel passiert solltest du jetzt wissen was zu tun ist. Weiterhin ist durch nutzung der ImageIO klassen und vorheriges laden des bildes niemals der fall gegeben das image == null ist sprich du kannst dir das if(img !=null) sparen.

Das „Phänomen“ das du das bild erst beim rumklicken auf dem desktop gesehen hat wurde dadurch ausgelöst das Swing die paint methoden von awt zwar irgendwo aufruft, aber nicht durch ein repaint(); Nur in speziellen fällen wird paint noch benutzt.

Also einfach immer paintComponent überschreiben, ausser :slight_smile: bei Container Objecten wie JPanels…

mfg,
Jens

Ok, danke!! Aber leider hat paintComponent auch nicht geholfen. Also wie vorher, das Bild wird nur einmal gezeichnet. Dann nicht mehr bzw. das alte bleibt immer erhalten… :frowning:

Hab das mit dem ImageIO mal versucht, was muß ich hier noch machen:

	File datei = new File( "x2.jpg" );
		Image image = ImageIO.read( datei );

So,

am paintCompont kann es nicht liegen! Wenn du es überschreibst ist das ok.
Wie sieht denn der Code aus der das neue bild gegen das alte austauscht ? Hier kann ja fast nur noch das problem liegen!

mfg,
Jens

Na ja, also so richtig ausgetauscht wird es nicht. Es wird halt immer

malen=new Zeichner();
GUIAnwedung.label1.add(malen);

durchgeführt. Sprich beim 1. mal und alle folgenden male.

Habe hier auch schon sämtliche Kombis mit:

 GUIAnwendung.label47.add(new Zeichner());
    GUIAnwendung.label47.revalidate();
    GUIAnwendung.label47.repaint();

versucht. JLayeredPane gegen JLabel ersetzt usw.

Wenn ich den Zeichner so sehe ist da dort hart reincodiert das er immer das selbe bild anzeigt. Und BITTE BITTE schaue dir an was wofür welche Komponente gedacht ist. So sätze wie “versucht. JLayeredPane gegen JLabel ersetzt usw.” lassen mir den schweiss kalt runterlaufen (verstehe das nicht als kritik, sondern als ansporn).

Ja du hast schon recht. Dieses ständige Experimentieren kostet extrem viel Zeit. Na ja, Vorteil: kein Mensch findet sich jemals mehr in meinem Source zu recht! :wink:

Also keine Ahnung, jetzt klappt es, hab einfach bei dem JLabel noch ein removeAll(); reingebastelt!

Du hast hier zwar JLayeredPane gegen JLabel ausgetauchst, aber so wie du das nutzt, kannst auch genausogut von JComponent ableiten.

Du solltest nicht die ganze Arbeit im Konstruktor machen, und bei einem JLabel kann man setIcon verwenden.

Ein ImageIcon kann man auch ohne einen Pfad instanzieren sondern mit einem Image, welches auch ruhig vorher skaliert werden kann.

Klassennamen schreibt man groß.

Und hier noch mal ein Beispiel was genau machen sollte was du brauchst, du musst halt von ImageLabel immer nur loadImage aufrufen wenn dein Button gedrückt wurde.

import java.awt.*;
import java.awt.event.*;


public class ImageLabel extends JLabel {



  public void loadImage(String filename){
    Image image = this.getToolkit().getImage(filename);
    image = image.getScaledInstance(this.getWidth(), this.getHeight(), Image.SCALE_SMOOTH);
    this.setIcon(new ImageIcon(image));
  }
  

  
  public static void main(final String[] args) {
    if (args.length==0) System.exit(1);
    
    final ImageLabel imageLabel = new ImageLabel();
  
    JButton button = new JButton("Nächstes Bild");
    button.addActionListener(new ActionListener() {
      private int index;
      public void actionPerformed(ActionEvent e) {
        imageLabel.loadImage(args[++index % args.length]);
      }
    });
   
    JFrame aFrame = new JFrame("Test");
    aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    aFrame.getContentPane().setLayout(new BorderLayout());
    aFrame.getContentPane().add(button, BorderLayout.SOUTH);
    aFrame.getContentPane().add(imageLabel, BorderLayout.CENTER);
    aFrame.setSize(400, 300);
    aFrame.setVisible(true);
  }
}```