JComponents zur Laufzeit auf JPanel darstellen

Thanathan

Mitglied
Guten Abend!

Ich habe ein (vorerst leeres) JPanel. Zur Laufzeit werden mehrere Objekte meiner Klasse "Blech" erstellt (welche von JComponent erbt), die ich dann auf diesem JPanel anzeigen lassen möchte.
Leider hab ich irgendwo noch ne Kleinigkeit übersehen, jedenfalls bleibt JPanel leer.

Hier erstmal "Blech":

Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.FileWriter;
import java.util.*;

import javax.swing.JComponent;

// Repräsentiert ein Blech,
// besteht aus Maßen fürs Blech und Liste von Formen
public class Blech extends JComponent
{
    private ArrayList<Form> formen;
    private float blechBreite;
    private float blechLaenge;
   
    public Blech(float breite, float laenge, ArrayList<Form> f)
    {
        formen = f;
        blechBreite = breite;
        blechLaenge = laenge;
    }
   
    // überschreibt die JComponent-Methode
    public void paint(Graphics g)
    {
       drawBlech((Graphics2D) g, getBounds());
    }
   
    void drawBlech(Graphics2D g, Rectangle area)
    {
       g.setColor(Color.white);
       g.fill(new Rectangle2D.Float((float)area.getX(), (float)area.getY(), blechBreite, blechLaenge));
      
       g.setColor(Color.black);
       for(int i = 0; i < formen.size(); i++)
       {
           // hier werden später die Formen aus formen einzeln aufgerufen und auf das Blech drübergemalt; vorerst zu vernachlässigen
       }
   }
}

Ich füge mehrere Bleche ins JPanel graphPanel mit der Funktion zeichneBleche. Unten hab ich erstmal Testsätze erstellt.

Java:
public void zeichneBleche()
    {
        int x = graphPanel.getX()+10;
        int y = graphPanel.getY()+10;
       
        // Test
        float[] floats1 = {(float)0.5, 2, 4}; float[] floats2 = {3, 4, 5};
        FormObjekt f1 = new FormObjekt(floats1, floats2, 1, 3, "Form1");
        float[] floats3 = {0, 2, 4}; float[] floats4 = {3, 4, 5};
        FormObjekt f2 = new FormObjekt(floats3, floats4, 1, 3, "Form2");
        float[] floats5 = {(float)0.5, 2, 4}; float[] floats6 = {3, 4, 5};
        FormObjekt f3 = new FormObjekt(floats5, floats6, 1, 3, "Form3");
        ArrayList<FormObjekt> fl1 = new ArrayList<FormObjekt>();
        fl1.add(f1);
        fl1.add(f2);
        fl1.add(f3);
        Blech b1 = new Blech(20, 10, fl1);
       
        FormObjekt f4 = new FormObjekt(floats1, floats2, 1, 3, "Form1");
        FormObjekt f5 = new FormObjekt(floats3, floats4, 1, 3, "Form2");
        FormObjekt f6 = new FormObjekt(floats5, floats6, 1, 3, "Form3");
        ArrayList<FormObjekt> fl2 = new ArrayList<FormObjekt>();
        fl2.add(f4);
        fl2.add(f5);
        fl2.add(f6);
        Blech b2 = new Blech(20, 10, fl2);     

        bleche.add(b1);
        bleche.add(b2);
        // Test
       
        for(int i = 0; i < bleche.size(); i++)
        {
            Blech b = bleche.get(i);
            graphPanel.add(b);
            b.setBounds(x,y,20,10);
            graphPanel.revalidate();
            graphPanel.repaint();
           
            x = x + 20 + 10;
        }
Wobei der Code da noch nicht durchpoliert ist, mir ist klar dass zb das b.setBounds() so noch sehr unschön ist, aber ich wollte erstmal die Grundlage haben und danach verbessern. 😀

bleche ist eine globale ArrayList<Blech> Variable.

zeichneBleche() rufe ich nach dem Druck auf einen Button auf. Im Debugger sehe ich auch, dass die for-Schleife vernünftig durchlaufen wird, aber das Panel bleibt weiterhin leer.
 
1. Bei JComponents sollte man nicht paint, sondern paintComponent(Graphics g) überschreiben. Der erste call in der überschriebenen paintComponent sollte super.paintComponent(g) sein.
2. Deine Bleche haben eine preferredSize von 1, 1. Du kannst das gerne per sysout überprüfen. Wenn der LayoutManager die preferredSize berücksichtigt, schrumpfen die Bleche beim layouten auf die Größe eines Pixels. Welchen LayoutManager du verwendest, ist aus deinem bisher geposteten Code nicht ersichtlich.
 
1.) Alles klar, hab ich geändert. Hatte allerdings erstmal keinen sichtbaren Effekt.
2.) Hmm, LayoutManager ist eigentlich auf null gesetzt. Auch wenn ich die preferredSize testweise ändere, ändert sich leider nichts am Ergebnis...
 
2.) Hmm, LayoutManager ist eigentlich auf null gesetzt. Auch wenn ich die preferredSize testweise ändere, ändert sich leider nichts am Ergebnis...

Da du keinen LayoutManager verwendest bringt eine Änderung von "preferredSize" nichts (ein LayoutManager würde diese Werte auslesen und umsetzen).
Verwende einen LayoutManager und du wirst deine Bleche sehen. Nimm Abstand vom null-Layout => http://www.fredosaurus.com/notes-java/GUI/layouts/nulllayout.html
 
Schade, ich mag eigentlich, dass man alle Teile beliebig positionieren kann. Aber gut, dann puzzle ich mir mal was zusammen^^
FlowLayout hat ebenfalls nichts gebracht, mal schauen ob BorderLayout mehr bringt. Momentan spuckt er mir noch ne Exception aus, muss wahrscheinlich noch den restlichen Code etwas anpassen.
 
Hast du meinen Beitrag gelesen?
LayoutManager ist eigentlich auf null gesetzt
FlowLayout hat ebenfalls nichts gebracht, mal schauen ob BorderLayout mehr bringt
Von welchem Container redest du? Vom graphPanel, oder dem Container in dem graphpanel liegt?
Einer JComponent (und das bedeutet auch: einem JPanel) kann man entweder eine feste preferredSize verpassen, oder man kann sie durch den LayoutManager berechnen lassen. Diese size kann durch den LayoutManager des Containers, in dem die JComponent liegt, berücksichtigt werden oder auch nicht.
Wenn dein graphPanel weder eine manuell gesetzte preferredSize noch einen LayoutManager hat, solltest du nur dann dein graphPanel (und die darin enthaltenen Bleche) sehen, wenn das graphPanel durch den Container in dem es liegt, eine sichtbare Größe bekommt.
 
Meine Umgebung, also der Container in dem alles liegt, ist ein JFrame mit null-Layout. Hatte das oben so verstanden, dass ich das Layout des JPanels graphPanel ändern sollte.
Bisher hatte ich eigentlich alles auf null um es exakt positionieren zu können.

Meines Wissens wird preferredSize() doch sowieso nur von LayoutManagern verwendet? Aus welchem Grund funktioniert dann hier setBounds() nicht? Das JPanel konnte ich ja auch manuell positionieren, wo ist der Unterschied zu meinen Blechen? Sie werden auch nicht als einzelne Pixel dargestellt, von daher hätte ich den Fehler eigentlich woanders vermutet...
Sorry, bin gerade etwas verwirrt, sehe wahrscheinlich den Wald vor lauter Bäumen nicht 😀
 
Meine Umgebung, also der Container in dem alles liegt, ist ein JFrame mit null-Layout.
Ich nehme an, "alles" beinhaltet auch das graphPanel? Wenn das JFrame ein null-Layout hat, musst du beim graphPanel auch die bounds manuell setzen. Davon ist aber in deinem Code nichts zu sehen. Wenn dein graphPanel seine bounds weder manuell noch durch den LayoutManager des JFrame gesetzt bekommt, sollte graphPanel eigentlich eine Größe von 1,1 oder 0, 0 haben. Es kann sein, dass dann auch Kindkomponenten von graphPanel nicht gezeichnet werden, selbst wenn diese Kindkomponenten bounds größer als 1,1 sind.
Allmählich bin ich aber auch verwirrt.
Zeige mal ein komplettes lauffähiges Program, was das falsche Verhalten zeigt.
 
Ja, ist vielleicht besser so. Sorry für die Verwirrung 😀

Java:
import javax.swing.*;
import java.io.File;
import java.io.FileWriter;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import java.awt.geom.Rectangle2D;
import java.lang.Math;

public class Gui extends JFrame implements ActionListener
{
    // GUI Komponenten
    private JButton waehleDateiButton;
    private JButton berechneLoesungButton;
    private JButton gebeTextfileButton;
    private JLabel dateiNameLabel;
    private JPanel panel; // Buttons etc.
    private JPanel graphPanel; // Anzeige der Ausgabe

    ArrayList<Blech> bleche = new ArrayList<>();
  
    public Gui()
    {
        // Erschaffen der Komponenten
        panel = new JPanel();
        graphPanel = new JPanel();
        dateiNameLabel = new JLabel();
        waehleDateiButton = new JButton("Wähle Fertigungsplan:");
        berechneLoesungButton = new JButton ("Berechne Anordnung");
        gebeTextfileButton = new JButton ("Ergebnis im Textformat");
        
        // Layout + Eventhandling
        panel.setBounds(10, 10, 500, 190);
        panel.setLayout(null);
        panel.setBackground(Color.red);
        panel.add(waehleDateiButton);
        panel.add(dateiNameLabel);
        panel.add(berechneLoesungButton);
        panel.add(gebeTextfileButton);
        graphPanel.setBackground(Color.green);
        graphPanel.setBounds(10,260,790,550);
        graphPanel.setLayout(new FlowLayout());
        waehleDateiButton.addActionListener(this);
        waehleDateiButton.setBounds(10,10,200,30);
        berechneLoesungButton.addActionListener(this);
        berechneLoesungButton.setBounds(10,50,200,30);
        gebeTextfileButton.addActionListener(this);
        gebeTextfileButton.setBounds(10,90,200,30);
        dateiNameLabel.setBounds(220,10,200,30);
      
        // Die Panels werden auf der GUI angezeigt
        Container contentPane = getContentPane();
        contentPane.add(panel, BorderLayout.NORTH);
        contentPane.add(graphPanel);
      
        // GUI Einstellungen
        this.setTitle("Titel");
        this.setSize(900,900);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(null);
        this.setVisible(true);
    }
   
    public void actionPerformed (ActionEvent ae)
    {
        if(ae.getSource() == this.waehleDateiButton)
        {
            // hiermit wird die Eingabedatei ausgewählt
        }
        else if(ae.getSource() == this.berechneLoesungButton)
        {
                try
                {   
                        this.zeichneBleche();
                }
                catch(Exception e){}
        }
        else if(ae.getSource() == this.gebeTextfileButton)
        {
            // andere Funktion
        }
    }
    
    public void zeichneBleche()
    {
        int x = graphPanel.getX()+10;
        int y = graphPanel.getY()+10;
      
        // Test
        float[] floats1 = {(float)0.5, 2, 4}; float[] floats2 = {3, 4, 5};
        Form f1 = new Form(floats1, floats2, 1, 3, "Form1");
        float[] floats3 = {0, 2, 4}; float[] floats4 = {3, 4, 5};
        Form f2 = new Form(floats3, floats4, 1, 3, "Form2");
        float[] floats5 = {(float)0.5, 2, 4}; float[] floats6 = {3, 4, 5};
        Form f3 = new Form(floats5, floats6, 1, 3, "Form3");
        ArrayList<Form> fl1 = new ArrayList<Form>();
        fl1.add(f1);
        fl1.add(f2);
        fl1.add(f3);
        Blech b1 = new Blech(20, 10, fl1);
      
        Form f4 = new Form(floats1, floats2, 1, 3, "Form1");
        Form f5 = new Form(floats3, floats4, 1, 3, "Form2");
        Form f6 = new Form(floats5, floats6, 1, 3, "Form3");
        ArrayList<Form> fl2 = new ArrayList<Form>();
        fl2.add(f4);
        fl2.add(f5);
        fl2.add(f6);
        Blech b2 = new Blech(20, 10, fl2);
      
        bleche.add(b1);
        bleche.add(b2);
        // Test
      
        for(int i = 0; i < bleche.size(); i++)
        {
            Blech b = bleche.get(i);
            b.setBounds(x,y,20,10);
            b.setSize(20,10);
            graphPanel.add(b);
            graphPanel.revalidate();
            graphPanel.repaint();
          
            x = x + 20 + 10;
        }
    }
}

(Gui wird einfach von meiner main-Methode initialisiert)
Java:
public class Applikation
{
    public static void main(String[] args)
    {
        Gui meineGui = new Gui();
        meineGui.setVisible(true);
    }
}

Also ich hab für graphPanel schon die bounds gesetzt. Die Panels und Buttons und alles werden auch korrekt angezeigt, nur die zeichneBleche-Methode bewirkt nichts sichtbares (also auch keine 1x1 Pixel Bleche).

Die "Blech" Klasse hatte ich oben ja schon geschrieben, nur getter/setter etc. weggelassen. Hätte den Fehler spontan eher dort vermutet, da ich mit anderen JComponents (den Buttons etc) ja keine Probleme hatte.

Danke schon mal im Voraus 😀
 
Ich habe in den letzten Tagen immer mal wieder hierüber gebrütet. Die Lösung ist ganz einfach:
Alle Koordinaten für Zeichenoperation in der paintComponent verwenden die linke obere Ecke als Ursprung. Die bounds beziehen sich allerdings auf die Koordinaten relativ zum Elterncontainer.
In der drawBlech-Methode muss es also einfach
Java:
g.fill(new Rectangle2D.Float(0.0f, 0.0f, blechBreite, blechLaenge));
heissen.
 

Neue Themen


Zurück
Oben