简体   繁体   中英

JTable dynamic JCheckBox needs 1 click to uncheck, but 2 clicks to check

I am fairly new to java, so please excuse me for any foolish mistakes i make ...

I have a JTable of 2 columns in which the 2nd column can have 4 types :

  • a column in which values cannot be changed
  • a column which can contain any text string
  • a column which contains JComboBoxes
  • a column which contains JCheckBoxes

The column types are dynamically since they depend on the data being received.

All types work fine, except the JCheckBox. When I replace the JCheckBox with a JComboBox with 2 choices, then it works fine. So i guess there is a problem in the code for the JCheckBox.

The problem is :

  • when the JCheckBox is checked, then I need only 1 click to uncheck it
  • however when the JCheckBox is unchecked, then I need to click it twice (not doubleclick) to check it

EDIT

An example project (as small as i can make it) :

    import java.awt.Component;
    import javax.swing.*;
    import javax.swing.event.*;
    import javax.swing.table.*;

    public class frmCheck extends JApplet
    {
        JTable mgrdData;
        DefaultTableModel mtableModel;

        public void init()
        {
            mtableModel = new DefaultTableModel();
            mtableModel.setColumnCount(2);
            mtableModel.setRowCount(4);
            mgrdData = new JTable(mtableModel);
            mgrdData.getSelectionModel().addListSelectionListener(new RowListener());
            add(mgrdData);
            for (int i=1;i<mtableModel.getRowCount();i++)
            {
                addCheck(1);
            }
        }

        private class RowListener implements ListSelectionListener
        {
            public void valueChanged(ListSelectionEvent event)
            {
                if (event.getValueIsAdjusting()) return;
            }
        }

        private void addCheck(int intCol)
        {
            mgrdData.getColumnModel().getColumn(intCol).setCellRenderer(new TableCellRenderer()
            {
                public Component getTableCellRendererComponent(JTable table,Object value,boolean isSelected,boolean isFocused,int row,int col)
                {
                    JCheckBox rendererComponent = new JCheckBox();
                    String strVal="";
                    if (value!=null) strVal = value.toString();
                    if (strVal.equals("1"))
                    {
                        rendererComponent.setSelected(true);
                    } else
                    {
                        rendererComponent.setSelected(false);
                    }
                    return rendererComponent;
                }
            });
            DefaultCellEditor cellEditor = new DefaultCellEditor(new JCheckBox());
            cellEditor.setClickCountToStart(1);
            cellEditor.addCellEditorListener(new CellEditorListener()
            {
                public void editingCanceled(ChangeEvent e) {}
                public void editingStopped(ChangeEvent e)
                {
                    JCheckBox checkBox = (JCheckBox)((DefaultCellEditor)e.getSource()).getComponent();
                    System.out.println("isSelected = " + checkBox.isSelected());
                    if (checkBox.isSelected())
                    {
                        System.out.println("Sent 0");
                    } else
                    {
                        System.out.println("Sent 1");
                    }
                }
            });
            mgrdData.getColumnModel().getColumn(intCol).setCellEditor(cellEditor);
        }
    }

The html page which i use to view the Applet is :

<html>
  <body>
    <applet code="frmCheck.class" width="1016" height="822"></applet>
  </body>
</html>

To make it an executable (application) you can add the following main() function to it (above the init() function, and below the declaration of mtableModel)

        public static void main(String[] args) {
        frmCheck myApplet = new frmCheck(); // define applet of interest
        Frame myFrame = new Frame("Applet Holder"); // create frame with title

        // Call applet's init method (since Java App does not
        // call it as a browser automatically does)
        myApplet.init();    

        // add applet to the frame
        myFrame.add(myApplet, BorderLayout.CENTER);
        myFrame.pack(); // set window to appropriate size (for its elements)
        myFrame.setVisible(true); // usual step to make frame visible

      } 

When you click on an unchecked checkbox, then the console shows :

isSelected = true
Sent 0

after that the checkbox is still unchecked when i click the checkbox again (thus for a second time), then the console shows :

isSelected = false
Sent 1

the checkbox flashes as checked for a short time, and then becomes unchecked again when i then click the checkbox again (thus for a third time), then it behaves like being clicked for the first time

I want the checkbox to : - send 1 when unchecked and clicked - Send 0 when checked and clicked

The real project is a bit more complex : by checking the JCheckBox I send a message to a device, which answers with its current state, which is processed and shown as a checked JCheckBox. The processing works fine as when I change the state in the device via another channel, then the JCheckBox shows the current state perfectly

I would like the JCheckBox to respond to 1 click :

  • when it's checked and I click it then it should send "REG SCH 4 = 0"
  • when it's unchecked and I click it then it should send "REG SCH 4 = 1"

Ok here is the code i have so far :

The JTable is created as follows :

JTable mgrdData;
DefaultTableModel mtableModel;
mtableModel = new DefaultTableModel(null,new String[0]);
mgrdData = new JTable(mtableModel);
mgrdData.getSelectionModel().addListSelectionListener(new RowListener());
mgrdData.setFillsViewportHeight(true);
String[] strHeader = {"Naam","Waarde"};
mtableModel.setColumnIdentifiers(strHeader);

The columns are configured as follows :

        addReadOnly(0);
//          addCombo(1,new String[]{"UIT","AAN"}); //this works fine
        addCheck(1);

Column 0 is readonly, and column 1 has 2 possible values where addCombo(1,new String[]{"UIT","AAN"}); perfectly works

The functions addReadOnly and addCombo and addCheck are as follows :

private void addReadOnly(int intCol)
{
    JTextField txtField = new JTextField();
    txtField.setEditable(false);
    DefaultCellEditor cellEditor = new DefaultCellEditor(txtField);
    mgrdData.getColumnModel().getColumn(intCol).setCellEditor(cellEditor);
}

private void addCombo(int intCol,final String[] strItems)
{
    //add combobox
    mgrdData.getColumnModel().getColumn(intCol).setCellRenderer(new TableCellRenderer()
    {
        public Component getTableCellRendererComponent(JTable table,Object value,boolean isSelected,boolean isFocused,int row,int col)
        {
            JComboBox rendererComponent = new JComboBox(strItems);
            if (value!=null) rendererComponent.setSelectedItem(value.toString());
            return rendererComponent;
        }
    });
    DefaultCellEditor cellEditor = new DefaultCellEditor(new JComboBox(strItems));
    cellEditor.setClickCountToStart(1);
    cellEditor.addCellEditorListener(new CellEditorListener()
    {
        private boolean blnChanged=false;
        public void editingCanceled(ChangeEvent e) {}
        public void editingStopped(ChangeEvent e)
        {
            if (blnChanged==true)
            {
                JComboBox comboBox = (JComboBox)((DefaultCellEditor)e.getSource()).getComponent(); 
                sendVal(String.valueOf(comboBox.getSelectedIndex()));
                blnChanged = false;
            } else
            {
                blnChanged = true; 
            } 
        }
    });
    mgrdData.getColumnModel().getColumn(intCol).setCellEditor(cellEditor);
}

private void addCheck(int intCol)
{
    //add checkbox
    mgrdData.getColumnModel().getColumn(intCol).setCellRenderer(new TableCellRenderer()
    {
        public Component getTableCellRendererComponent(JTable table,Object value,boolean isSelected,boolean isFocused,int row,int col)
        {
            JCheckBox rendererComponent = new JCheckBox();
            String strVal="";
            if (value!=null) strVal = value.toString();
//              if (strVal.equals("AAN"))
            if (strVal.equals("1"))
            {
                rendererComponent.setSelected(true);
            } else
            {
                rendererComponent.setSelected(false);
            }
            return rendererComponent;
        }
    });
    DefaultCellEditor cellEditor = new DefaultCellEditor(new JCheckBox());
    cellEditor.setClickCountToStart(1);
    cellEditor.addCellEditorListener(new CellEditorListener()
    {
        public void editingCanceled(ChangeEvent e) {}
        public void editingStopped(ChangeEvent e)
        {
            JCheckBox checkBox = (JCheckBox)((DefaultCellEditor)e.getSource()).getComponent();
            System.out.println("isSelected = " + checkBox.isSelected());
            if (checkBox.isSelected())
            {
                sendVal("0");
                System.out.println("Sent 0");
            } else
            {
                sendVal("1");
                System.out.println("Sent 1");
            }
        }
    });
    mgrdData.getColumnModel().getColumn(intCol).setCellEditor(cellEditor);
}

Test results after 3 times clicking :

JCheckBox is checked, result after 1 click :

isSelected = true
Sent : REG SCH 4 = 0
Sent 0

After this the JCheckBox is unchecked

JCheckBox is unchecked, result after 1 click :

isSelected = true
Sent : REG SCH 4 = 0
Sent 0

After this the JCheckBox is still unchecked

JCheckBox is unchecked but clicked once already, result after the second click :

isSelected = false
Sent : REG SCH 4 = 1
Sent 1

After this the JCheckBox is checked

I search and found solution for when you always needed 2 clicks, or solutions with fixed checkboxes instead of dynamically, but not a case like mine ....

could anyone please shed a light ?

Answer

Problem was that you were treating the checkbox value as a String when it's a boolean.

Working code is the following:

Boolean val = new Boolean(false);
if (value != null){
    val = (Boolean) value;
}
if (val.booleanValue()) {
    rendererComponent.setSelected(true);
}else{
     rendererComponent.setSelected(false);
}
return rendererComponent;

Old answer

I'm still looking at it, but I think the problem might be due to the fact that the JCheckBox is selected or not depending on:

if (strVal.equals("AAN"))
        {
            rendererComponent.setSelected(true);
        } else
        {
            rendererComponent.setSelected(false);
        }

Isn't "AAN" a value for the dropbox instead than for the checkbutton?

An also, you're creating a new JCheckBox each time.

Maybe a SSCCE could be useful.

The problem with your code is in getTableCellRendererComponent method. Here you are always creating a new component with JCheckBox . So every time it is taking a new JCheckBox and rendering the column.

In addition to that there is no need to render a column to Boolean . JTable it self will render that. You just need to specify the column type as Boolean.class. Overwrite the getColumnClass method in the model (DefaultTableModel, like I have shown) and say that column-1 is a Boolean.

Applet Exampel

import javax.swing.DefaultCellEditor;
import javax.swing.JApplet;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

    public class frmCheck extends JApplet
    {
        JTable mgrdData;
        DefaultTableModel mtableModel;

        public void init()
        {
            mtableModel = new DefaultTableModel() {
                @Override
                public Class<?> getColumnClass(int arg0) {
                    if(arg0 == 1) {
                        return Boolean.class; 
                    } else {
                        return super.getColumnClass(arg0);
                    }
                }
            };
            mtableModel.setColumnCount(2);
            mtableModel.setRowCount(4);
            mgrdData = new JTable(mtableModel);
//            mgrdData.getSelectionModel().addListSelectionListener(new RowListener());
            add(mgrdData);

            for (int i=1;i<mtableModel.getRowCount();i++)
            {
                addCheck(1);
            }
        }

        private class RowListener implements ListSelectionListener
        {
            public void valueChanged(ListSelectionEvent event)
            {
                if (event.getValueIsAdjusting()) return;
            }
        }

        private void addCheck(int intCol)
        {

            DefaultCellEditor cellEditor = new DefaultCellEditor(new JCheckBox());
             mgrdData.getColumnModel().getColumn(1).getCellEditor();
            cellEditor.setClickCountToStart(1);
            cellEditor.addCellEditorListener(new CellEditorListener()
            {
                public void editingCanceled(ChangeEvent e) {}
                public void editingStopped(ChangeEvent e)
                {
                    JCheckBox checkBox = (JCheckBox)((DefaultCellEditor)e.getSource()).getComponent();
                    System.out.println("isSelected = " + checkBox.isSelected());
                    if (checkBox.isSelected())
                    {
                        System.out.println("Sent 0");
                    } else
                    {
                        System.out.println("Sent 1");
                    }
                }
            });
            mgrdData.getColumnModel().getColumn(intCol).setCellEditor(cellEditor);
        }
    }

PS: I have removed the unused code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM