JTable Checkbox

Hallo Leute!!

Würde mir bitte jemand bei meinem Problem helfen? Ich programmiere noch nicht lange, bin aber bei meiner Projektarbeit schon seit Tagen an einem Punkt wo ich nicht weitermachen kann. Leider fehlt mir offensichtlich das Know-How bei einigen kleinigkeiten, die’s aber scheinbar ausmachen.

Die Oracle Tutorials habe ich schon durch und viele weitere Seiten auch. Ich komme aber dennoch nicht dahinter wo das Problem liegt, dass ich in meiner JTable, in eine Checkbox, kein Häckchen machen kann. Editieren kann man es mit einem Doppelklick aber auch wenn ich „true“ reinschreibe kommt keins dieser Häckchen :confused:

Wäre vielleicht jemand so nett und würde über meinen Code schaun (ist nicht lauffähig)? Ich würde dringend Hilfe benötigen!

Aktueller Zustand:

Leider keine Häckchen, sondern bei einem Doppelklick sehe ich nur „false“ …

     
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.Insets;
    import java.awt.SystemColor;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.SQLException;
    import java.util.ArrayList;
     
    import javax.swing.BorderFactory;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JSplitPane;
    import javax.swing.JTable;
    import javax.swing.WindowConstants;
    import javax.swing.table.AbstractTableModel;
    import javax.swing.table.DefaultTableColumnModel;
    import javax.swing.table.TableColumn;
     
    import logik.DatenbankVerbindung;
     
    public class Table1 implements ActionListener{
     
        private JFrame jFrame = new JFrame();
     
        private DatenbankVerbindung dbv = new DatenbankVerbindung();
        private JPanel aa = new JPanel(new BorderLayout());
        private JPanel bb = new JPanel(new BorderLayout());
     
        private JButton add = new JButton("Bearbeiten");
        private JButton delete = new JButton("Loeschen");
     
        private JSplitPane splitPane;
        private JTable tbl1, tbl2;
        private JScrollPane scrollPane1, scrollPane2;
     
        private GridBagLayout gbl = new GridBagLayout();
        private GridBagConstraints gbc = new GridBagConstraints();
        private JPanel buttonPanel = new JPanel(gbl);
     
        private String produkt = "";
     
        public Table1(int id, String produkt) {
     
            jFrame.setTitle(produkt);
            this.produkt = produkt;
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.insets = new Insets (5,0,30,5);
            gbc.anchor = GridBagConstraints.EAST;
            add.setPreferredSize(new Dimension(130, 25));
            add.addActionListener(this);
            gbl.setConstraints(add, gbc);
            buttonPanel.add(add);
     
            gbc.gridx = 1;
            gbc.gridy = 0;
            gbc.insets = new Insets (5,5,30,0);
            gbc.anchor = GridBagConstraints.WEST;
            delete.setPreferredSize(new Dimension(130, 25));
            delete.addActionListener(this);
            gbl.setConstraints(delete, gbc);
            buttonPanel.add(delete);
     
            dbv.verbinden();
            ResultSet rs = dbv.produktAuflistung(produkt);
            TableModelFromRS tmfr = new TableModelFromRS(rs);
            DefaultTableColumnModel dtcm = tmfr.GetColumnModel();
            tbl1 = new JTable(tmfr, dtcm);
            tbl2 = new JTable();
            dtcm.getColumn(0).setCellRenderer(tbl1.getDefaultRenderer(Boolean.class));
            scrollPane1 = new JScrollPane(tbl1);
            scrollPane2 = new JScrollPane(tbl2);
            scrollPane1.getVerticalScrollBar().setUnitIncrement(15);
            scrollPane2.getVerticalScrollBar().setUnitIncrement(15);
     
     
            aa.add(BorderLayout.CENTER, tbl2);
            aa.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
            aa.setPreferredSize(new Dimension(800, 325));
     
            bb.add(BorderLayout.SOUTH, buttonPanel);
            bb.add(BorderLayout.CENTER, scrollPane1);
            bb.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
            bb.setPreferredSize(new Dimension(800, 375));
     
            splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, bb, aa);
     
            splitPane.setDividerLocation(0.70);
            splitPane.setDividerSize(0);
            splitPane.setResizeWeight(0.70);
            splitPane.setEnabled(false);
     
            jFrame.setSize(850, 700);
            jFrame.setLocationRelativeTo(null);
            jFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            jFrame.setBackground(SystemColor.window);
            jFrame.setContentPane(splitPane);
            jFrame.setVisible(true);
        }
     
        @Override
        public void actionPerformed(ActionEvent e) {
            dbv.verbinden();
            ResultSet rs = dbv.produktAuflistung(produkt);
            String[] spaltenNamen = null;
            int rsSpaltenAnzahl = 0;
            ResultSetMetaData metaData;
            int [] selectetRows = null;
            try {
                if (e.getSource() == add){
     
                    metaData = rs.getMetaData();
                    rsSpaltenAnzahl = metaData.getColumnCount();
                    spaltenNamen = new String[rsSpaltenAnzahl];
                    selectetRows = tbl1.getSelectedRows();
                }
            } catch (SQLException e1) {
                e1.printStackTrace();
            }  
            for(int i = 0; i < spaltenNamen.length; i++) {
                TableColumn tc = new TableColumn(i);
                tc.setHeaderValue(spaltenNamen**);
            }
            jFrame.repaint();
           
           
        }
     
     
        class TableModelFromRS extends AbstractTableModel {
     
            private static final long serialVersionUID = 1L;
            private String[] columName;
            private ArrayList<Object[]> zeilenList;
            TableModelFromRS(ResultSet rs) {
     
                ResultSetMetaData metaData;                 // Metadaten - beinhalten alle infos zur Tabelle
                try {
                    metaData = rs.getMetaData();
                    // Anzahl der Zeilen im Resultset
                    int rsSpaltenAnzahl = metaData.getColumnCount() + 1;
                    columName = new String[rsSpaltenAnzahl];
     
                    columName[0] = "ADD";                           //Erste Spalte ein Boolean
                    for (int i = 1; i < rsSpaltenAnzahl; ++i) {            
                        columName** = metaData.getColumnLabel(i);      // Beinhaltet jetzt alle SpaltenNamen
                        System.out.println(columName**);
                    }
                    // Alle Zeilen abspeichern
                    zeilenList = new ArrayList<Object[]>();             //neue ArrayList bestehend aus Objekt Arrays
     
                    while (rs.next()) {                                     // solange spalten im Resultset sind ...
                        Object[] zeilenArray = new Object[rsSpaltenAnzahl]; // ... wird jeweils ein neues Array vom Typ Objekt erstellt mit der Länge der Spalten im Resultset [rsSpaltenAnzahl]   
                        // jeweils in die Erste Spalte wird ein Boolean wert angelegt.
                        zeilenArray[0] = new Boolean(false);  // False, weil nicht markiert
     
                        for (int i = 1; i < rsSpaltenAnzahl; i++) {         // Jede Zeile einzeln einlesen
                            zeilenArray** = rs.getObject(i);
                        }
                        zeilenList.add(zeilenArray);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
     
            @Override
            public int getRowCount() {
                return zeilenList.size();
            }
     
            @Override
            public int getColumnCount() {
                return columName.length;
            }
     
            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                return zeilenList.get(rowIndex)[columnIndex];
            }
     
            public boolean isCellEditable(int row, int column) {
                if(column == 0)
                    return true;
                else
                    return false;
            }
     
            public DefaultTableColumnModel GetColumnModel() {
                DefaultTableColumnModel dtcm = new DefaultTableColumnModel();
                for(int i = 0; i < columName.length; i++) {
                    TableColumn tc = new TableColumn(i);
                    tc.setHeaderValue(columName**);
                    dtcm.addColumn(tc);
                }
                return dtcm;
            }
     
        }
    }```

Vielen lieben Dank!

es ist nicht schwer in Suchmaschinen nach JTable und Boolean oder Checkbox zu suchen,

auch könntest du dir anschauen, was du von AbstractTableModel noch nicht alles übernommen hast,
da gibt es übrigens auch getColumnName(column), ein einfacherer Weg, die Spaltennamen einzubinden

für die Normalimplementierung muss getColumnClass() Boolean für die Spalte liefern,
außerdem brauchst du noch eine setValueAt()-Methode, um die übergebenen Werte auch abzuspeichen


allgemein:
a) ein Tablemodel ist nicht ohne Grund einfach strukturiert aufgebaut, Listen von Daten,

die Auswertung des ResultSet gehört nicht in das TableModel, das kann irgendeine Methode machen,
wenn dann ColumnNames und Daten feststehen, dann diese an das TableModel übergeben

b) insgesamt ist dein Programm zwar ausreichend, den Fehler zu finden, aber nicht gerade leser-optimiert,
überlege dir, wieviel Arbeit du selber reinstecken kannst um sie anderen zu ersparen,
das wäre wichtiger als Hallo + Danke :wink:

ResultSet kann niemand testen, die actionPerformed-Methode ist überflüssig, das Layout könnte testweise viel simpler sein,
eine main-Methode fehlt!,
mit differenzierten ResultSet wäre es leichert, das Programm mit anderen Daten zu testen,
ich habe mir jetzt auch nicht die volle Mühe gemacht, die Dummy-Daten ebenso im Konstruktor eingefügt

natürlich nach/ während solcher Kürzungen prüfen, ob der Fehler überhaupt noch vorliegt,
wenn Fehler weg dann gar schon halb selbst gelöst

sowas könnte herauskommen (inklusive Korrekturen) :

{
    public static void main(String[] args)
    {
        new Table1(1, "a");
    }
}

class Table1
{
    private JFrame jFrame = new JFrame();

    public Table1(int id, String produkt)
    {
        jFrame.setTitle(produkt);

        TableModelFromRS tmfr = new TableModelFromRS();
        DefaultTableColumnModel dtcm = tmfr.GetColumnModel();
        JTable tbl1 = new JTable(tmfr, dtcm);
        jFrame.add(new JScrollPane(tbl1));

        jFrame.setSize(450, 200);
        jFrame.setLocationRelativeTo(null);
        jFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        jFrame.setVisible(true);
    }

    class TableModelFromRS
        extends AbstractTableModel
    {
        private static final long serialVersionUID = 1L;
        private Class[] cClass = new Class[]
            {Boolean.class, String.class};
        private String[] columName2;
        private ArrayList<Object[]> zeilenList;

        TableModelFromRS()
        {
            // Anzahl der Zeilen im Resultset
            int rsSpaltenAnzahl = 2;
            columName2 = new String[rsSpaltenAnzahl];

            columName2[0] = "ADD"; // Erste Spalte ein Boolean
            for (int i = 1; i < rsSpaltenAnzahl; ++i)
            {
                columName2** = "test";
                System.out.println(columName2**);
            }
            // Alle Zeilen abspeichern
            zeilenList = new ArrayList<Object[]>();

            for (int j = 0; j < 5; j++)
            { // solange spalten im Resultset sind ...
                Object[] zeilenArray = new Object[rsSpaltenAnzahl];
                // jeweils in die Erste Spalte wird ein Boolean wert angelegt.
                zeilenArray[0] = Boolean.FALSE; // False, weil nicht markiert

                for (int i = 1; i < rsSpaltenAnzahl; i++)
                { // Jede Zeile einzeln einlesen
                    zeilenArray** = "test " + j;
                }
                zeilenList.add(zeilenArray);
            }
        }

        @Override
        public Class<?> getColumnClass(int columnIndex)
        {
            return cClass[columnIndex];
        }

        public int getRowCount()
        {
            return zeilenList.size();
        }

        public int getColumnCount()
        {
            return columName2.length;
        }

        public Object getValueAt(int rowIndex, int columnIndex)
        {
            return zeilenList.get(rowIndex)[columnIndex];
        }

        public void setValueAt(Object aValue, int rowIndex, int columnIndex)
        {
            if (columnIndex == 0) zeilenList.get(rowIndex)[0] = (Boolean)aValue;
        }

        public boolean isCellEditable(int row, int column)
        {
            if (column == 0)
                return true;
            else
                return false;
        }

        public DefaultTableColumnModel GetColumnModel()
        {
            DefaultTableColumnModel dtcm = new DefaultTableColumnModel();
            for (int i = 0; i < columName2.length; i++)
            {
                TableColumn tc = new TableColumn(i);
                tc.setHeaderValue(columName2**);
                dtcm.addColumn(tc);
            }
            return dtcm;
        }

    }
}

edit: new Boolean(false) braucht man übrigens nicht, ein Boolean ist (wie String) unveränderlich, die Konstante Boolean.FALSE tut es genauso

Du könntest ja mal schauen was in dieser Zeile drinsteht (System.out.println von Wert + Klasse des Werts)

Nur überflogen, aber das Beispiel von http://docs.oracle.com/javase/tutorial/uiswing/components/table.html verwendet ja schon CheckBoxes - funktioniert DAS denn bei dir?

@MrNice
zu deiner PN

‘Exception in thread “AWT-EventQueue-0” java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean’
kann es mit meinem Code direkt verwendet offensichtlich nicht geben,

wenn du es bei dir irgendwo einbaust, musst du es korrekt tun, kein Boolean als ColumnClass für eine Spalte, die letztlich Strings enthält,
ohne Code schwer weiter zu erraten

    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

könnte noch gute Alternative aus dem Link von Marco13 sein statt die Klassen direkt vorzugeben,

Bedeutung offensichtlich: einen Wert der fraglichen Spalten holen, dessen Klasse zurückgeben,
nur Pech falls ResultSet und dann Tabelle mal leer sein sollte…


und deine sonstigen Angaben sind bedenklich, einen klaren Kopf zu behalten ist die oberste Aufgabe,
sinnlos Java-Befehle hinzuschreiben, evtl. von Vorlagen zu kopieren, kann nur falsch enden

man muss nicht alles wissen, aber das was man verwendet dann mit einigermaßen Bedacht,
mit Ziel und wenn schon nicht richtig dann zumindest angenommer richtiger Wirkung jedes Befehls, jeder überschriebenen Methode usw.,
jede Codezeile erklären können,

‘alle Suchergebnisse schon lila’ führt zu gar nix

Danke an euch alle!
Vor allem aber an SlaterB! Genau diese Zeile wars, die du zum Schluss geschrieben hast =)
Die Checkboxen sind endlich auch zum Klicken.
Und jetzt versteh ich auch, warum das der Fall ist. Der Renderer zeichnet die Tabelle ohne zu wissen was ne checkbox ist. Erst die getColumnClass Methode sagt dem Tablemodel was diese Spalte darstellen soll.
Stimmt das in etwa?

Nice!

die normale Darstellung war bisher ja auch schon eine CheckBox, der Renderer dafür schaut nach dem konkreten Objekt (Boolean),
fürs Bearbeiten gibt es noch eine Editor-Komponente, dann nach Spalte, ja, das ist der Normalweg

alternativ kann man auch eigene Zusatz-Komponenten schreiben und Anzeige/ Edit wild je nach Zelle durcheinander gestalten