Для динамического JTable JCheckBox требуется 1 щелчок, чтобы снять флажок, и 2 щелчка, чтобы проверить

Я довольно новичок в Java, поэтому, пожалуйста, извините меня за любые глупые ошибки, которые я делаю...

У меня есть JTable из 2 столбцов, в которых 2-й столбец может иметь 4 типа:

  • столбец, в котором значения не могут быть изменены
  • столбец, который может содержать любую текстовую строку
  • столбец, содержащий JComboBoxes
  • столбец, содержащий JCheckBoxes

Типы столбцов являются динамическими, поскольку они зависят от получаемых данных.

Все типы работают нормально, кроме JCheckBox. Когда я заменяю JCheckBox на JComboBox с двумя вариантами выбора, все работает нормально. Так что я предполагаю, что есть проблема в коде для JCheckBox.

Проблема в :

  • когда JCheckBox отмечен, мне нужно всего 1 щелчок, чтобы снять его
  • однако, когда JCheckBox не отмечен, мне нужно дважды щелкнуть по нему (не дважды), чтобы проверить его

РЕДАКТИРОВАТЬ

Пример проекта (насколько я могу его сделать):

    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);
        }
    }

HTML-страница, которую я использую для просмотра апплета:

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

Чтобы сделать его исполняемым (приложением), вы можете добавить к нему следующую функцию main() (над функцией init() и ниже объявления 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

      } 

Когда вы нажимаете на снятый флажок, консоль показывает:

isSelected = true
Sent 0

после этого флажок по-прежнему не установлен, когда я снова нажимаю флажок (таким образом, во второй раз), затем консоль показывает:

isSelected = false
Sent 1

флажок мигает как отмеченный в течение короткого времени, а затем снова становится неотмеченным, когда я снова щелкаю флажок (таким образом, в третий раз), затем он ведет себя так, как будто его щелкнули в первый раз.

Я хочу, чтобы флажок: - отправлял 1 при снятии флажка и нажатии - отправлял 0 при отметке и нажатии

Реальный проект немного сложнее: проверяя JCheckBox, я отправляю сообщение на устройство, которое отвечает своим текущим состоянием, которое обрабатывается и отображается как проверенный JCheckBox. Обработка работает нормально, так как когда я меняю состояние в устройстве по другому каналу, то JCheckBox отлично показывает текущее состояние

Я хотел бы, чтобы JCheckBox реагировал на 1 клик:

  • когда он отмечен, и я нажимаю на него, он должен отправить «REG SCH 4 = 0»
  • когда он не отмечен, и я нажимаю на него, он должен отправить «REG SCH 4 = 1»

Хорошо, вот код, который у меня есть:

JTable создается следующим образом:

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);

Колонки настроены следующим образом:

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

Столбец 0 доступен только для чтения, а столбец 1 имеет 2 возможных значения, где addCombo(1,new String[]{"UIT","AAN"}); отлично работает

Функции addReadOnly, addCombo и addCheck следующие:

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);
}

Результаты тестирования после 3-кратного нажатия:

JCheckBox проверен, результат после 1 клика:

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

После этого JCheckBox снимается

JCheckBox не отмечен, результат после 1 клика:

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

После этого JCheckBox по-прежнему не отмечен

JCheckBox не отмечен, но уже нажат один раз, результат после второго щелчка:

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

После этого проверяется JCheckBox

Я искал и нашел решение, когда вам всегда нужно было 2 клика, или решения с фиксированными флажками вместо динамических, но не такой случай, как мой ....

может ли кто-нибудь пролить свет?


person Hrqls    schedule 23.01.2013    source источник
comment
Можете ли вы опубликовать пример кода с указанием проблемы?   -  person Amarnath    schedule 23.01.2013
comment
приведенный выше код является образцом из класса ... я думаю, что проблема в коде addCheckBox, но я не уверен ... это также может быть в объявлении таблицы или модели таблицы ... проблема в том, что я щелкаю флажок один раз он не отмечен (как и должно быть), но затем мне нужно дважды щелкнуть по нему, чтобы проверить его снова ... или вы имеете в виду новый простой проект только с таким поведением?   -  person Hrqls    schedule 24.01.2013
comment
Вы имеете в виду новый простой проект с таким поведением Точно. Becoz читая все это, я думаю, мы не сможем решить. Просто опубликуйте небольшую программу, которая показывает проблему.   -  person Amarnath    schedule 24.01.2013
comment
хорошо, сейчас создам...   -  person Hrqls    schedule 24.01.2013
comment
я добавил образец проекта в первый пост (первый блок кода, с которым вы столкнулись)   -  person Hrqls    schedule 24.01.2013
comment
Пожалуйста, напишите исполняемый код. Так что я могу запустить и проверить проблему.   -  person Amarnath    schedule 24.01.2013
comment
это апплет. Я скомпилировал его и смог открыть на веб-странице и воспроизвести ошибку... я попытаюсь создать приложение, но у меня мало/нет опыта в этом   -  person Hrqls    schedule 24.01.2013
comment
давайте продолжим это обсуждение в чате   -  person Amarnath    schedule 24.01.2013
comment
Пожалуйста, взгляните на мой пост.   -  person Amarnath    schedule 24.01.2013


Ответы (2)


Отвечать

Проблема заключалась в том, что вы обрабатывали значение флажка как строку, когда это логическое значение.

Рабочий код следующий:

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

Старый ответ

Я все еще смотрю на это, но я думаю, что проблема может быть связана с тем, что JCheckBox выбрано или нет, в зависимости от:

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

Разве "AAN" не является значением для раскрывающегося списка, а не для кнопки-флажка?

Кроме того, вы каждый раз создаете новый JCheckBox.

Возможно, будет полезен SSCCE.

person Alfergon    schedule 23.01.2013
comment
вы правы, AAN - это пережиток того времени, когда я использовал поле со списком ... но когда я меняю его на 0 и 1, проблема та же самая ... я отредактирую код в своем посте, чтобы показать 0 и 1 - person Hrqls; 24.01.2013
comment
Я закомментировал строку с AAN и заменил ее на 1, попробовал и получил те же результаты, я отредактировал код в своем посте. - person Hrqls; 24.01.2013
comment
Я сделаю пример проекта и надеюсь, что смогу воспроизвести проблему с минимальным количеством кода :) - person Hrqls; 24.01.2013
comment
я добавил образец проекта в первый пост (первый блок кода, с которым вы столкнулись) - person Hrqls; 24.01.2013
comment
Благодарность!! это было! (и поскольку теперь проверенное состояние изменяется перед вызовом editStopped, мне пришлось изменить там 0 и 1) ... теперь он отлично работает! Благодарность :) - person Hrqls; 24.01.2013
comment
Один вопрос по этому поводу: я получаю строковое значение от устройства, в случае данных типа флажка это либо 0, либо 1... мне нужно отправить логическое значение с помощью mtableModel.setValueAt() или я могу выполнить то же самое со строковым значением? - person Hrqls; 24.01.2013
comment
Протестировано: мне действительно нужно отправить логическую переменную... которую я не мог напрямую преобразовать из строковой переменной, но решил это с помощью дополнительной функции... спасибо за вашу помощь и решение :) - person Hrqls; 24.01.2013

Проблема с вашим кодом в методе getTableCellRendererComponent. Здесь вы всегда создаете новый компонент с помощью JCheckBox. Поэтому каждый раз он берет новый JCheckBox и отображает столбец.

В дополнение к этому нет необходимости отображать столбец в Boolean. JTable он сам это сделает. Вам просто нужно указать тип столбца как Boolean.class. Перезаписать метод getColumnClass в модели (DefaultTableModel, как я показал) и сказать, что столбец-1 является логическим.

Пример апплета

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);
        }
    }

P.S. Неиспользуемый код удалил.

person Amarnath    schedule 24.01.2013
comment
проблема в том, что столбец 1 не всегда будет флажком... в зависимости от значений, которые я получаю, я либо настрою его как значение только для чтения, либо поле со списком, либо флажок, либо текстовое поле... поэтому я сначала получит некоторые данные, которые сообщат мне, какой тип столбца, а после этого я получу сами данные, которые будут отображаться в этом столбце .... могу ли я динамически изменить переопределение для класса столбца? - person Hrqls; 24.01.2013
comment
Да, я думаю, что это возможно. Я сделал нечто подобное здесь. Используйте панель, которая удерживает компонент в зависимости от состояния. - person Amarnath; 24.01.2013
comment
Благодарность! я буду играть с этим позже. на данный момент я использую ответ @Alfergon, так как он требует небольших изменений в моем коде .. в будущей версии я попытаюсь преобразовать свой код в объявление вашей таблицы - person Hrqls; 24.01.2013