Радиокнопка в JTable не работает должным образом

У меня проблема со следующим кодом. Моя задача: у меня должны быть переключатели в первом столбце, и когда пользователь выбирает этот переключатель, эта строка выбирается и отправляется для обработки. Но моя проблема в том, что я могу выбрать переключатель, который находится в первом столбце, but afterwards when user clicks in any part of the table then my clicked radio button is being unchecked. я не могу понять, почему это происходит. Я действительно застрял с этой проблемой. Требуется помощь. Следующий код показывает мою проблему.

import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;

public class DisplayTable extends JDialog {
public void initialize() {
    SourceTableModel stm = new SourceTableModel();
    JTable sourceTable = new JTable(stm);

    sourceTable.getColumnModel().getColumn(0).setCellRenderer(new RadioButtonRenderer());
    sourceTable.getColumnModel().getColumn(0).setCellEditor(new RadioButtonEditor(new JCheckBox ()));

    JPanel panel = new JPanel();
    panel.add(new JScrollPane(sourceTable));
    add(panel);

    setModal(true);
    pack();
    setVisible(true);
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            new DisplayTable().initialize();
        }
    });
}
}


class SourceTableModel extends AbstractTableModel{

private static final long serialVersionUID = 1L;

private List<SourceModel> sourceList = new ArrayList<SourceModel>(); 
private String[] columnNamesList = {"Select", "Group", "Work"};

public SourceTableModel() {
    this.sourceList = getSourceDOList();
}

public String getColumnName(int column) {
    return columnNamesList[column];
}

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

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

public Class<?> getColumnClass(int columnIndex) {
    return (columnIndex == 0 ? Boolean.class : String.class);
}

public boolean isCellEditable(int rowIndex, int columnIndex) {
    return (columnIndex == 0 ? true : false);
}

public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    SourceModel model = (SourceModel) sourceList.get(rowIndex);
    switch (columnIndex) {
    case 0: 
        model.setSelect((Boolean)aValue);
        break;
    case 1: 
        model.setFactory((String) aValue);
        break;
    case 2: 
        model.setSupplier((String) aValue);
        break;
    }
    fireTableCellUpdated(rowIndex, columnIndex);
}


public Object getValueAt(int rowIndex, int columnIndex) {
    SourceModel source = sourceList.get(rowIndex);
    switch(columnIndex){
    case 0:
        return source.isSelect();
    case 1:
        return source.getFactory();
    case 2:
        return source.getSupplier();
    default:
        return null;
    }
}

private List<SourceModel> getSourceDOList() {
    List<SourceModel> tempSourceList=new ArrayList<SourceModel>();
    for (int index = 0; index < 5; index++) {

        SourceModel source = new SourceModel();
        source.setSelect(false);
        source.setFactory("group");
        source.setSupplier("Work");

        tempSourceList.add(source);
    }
    return tempSourceList;
}
}


class SourceModel {

private boolean select;
private String factory;
private String supplier;

public SourceModel() {
    // No Code;
}

public SourceModel(boolean select, String factory, String supplier) {
    super();
    this.select = select;
    this.factory = factory;
    this.supplier = supplier;
}

public boolean isSelect() {
    return select;
}

public void setSelect(boolean select) {
    this.select = select;
}

public String getFactory() {
    return factory;
}

public void setFactory(String factory) {
    this.factory = factory;
}

public String getSupplier() {
    return supplier;
}

public void setSupplier(String supplier) {
    this.supplier = supplier;
}
}

class RadioButtonEditor extends DefaultCellEditor implements ItemListener {

public JRadioButton btn = new JRadioButton();

public RadioButtonEditor(JCheckBox checkBox) {
    super(checkBox);
}

public Component getTableCellEditorComponent(JTable table, Object 
value, boolean isSelected, int row, int column) {

if (value==null) 
          return null;
btn.addItemListener(this);
if (( (Boolean) value).booleanValue())
    btn.setSelected(true);
else
    btn.setSelected(false);

    return btn;
}

public Object getCellEditorValue() {
    if(btn.isSelected() == true)
        return new Boolean(true);
    else 
        return new Boolean(false);
}

public void itemStateChanged(ItemEvent e) {
    super.fireEditingStopped();
}
}

class RadioButtonRenderer implements TableCellRenderer {
  public JRadioButton btn = new JRadioButton();
  public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int column) {
      if (value==null) return null;

      if(((Boolean)value).booleanValue())
      btn.setSelected(true);
      else
      btn.setSelected(false);

      if (isSelected) {
      btn.setForeground(table.getSelectionForeground());
      btn.setBackground(table.getSelectionBackground());
      } else {
      btn.setForeground(table.getForeground());
      btn.setBackground(table.getBackground());
      } 
      return btn;

  }
}

EDIT: я обновил свой код и использовал логический класс для первого столбца. The problem which I am facing is, if I remove super.fireEditingStopped(); from RadioButtonEditor class then I am able to check and then if I click at any part of the table then checked one I being unchecked. Если я оставлю super.fireEditingStopped(); то я даже не могу проверить радиокнопку.

Я знаю, что super.fireEditingStopped(); прекратит редактирование. Но у меня вопрос, как это проверить?

P.S. Извините, я разместил весь свой код. Я думал, что кому-то будет легко взглянуть на проблему.

Это скриншот программы. введите здесь описание изображения


person Amarnath    schedule 17.10.2012    source источник
comment
(вероятно, из-за плохого чтения этого неоптимально отформатированного кода), не связанного с вашей проблемой: реализация редактора недействительна (он должен уведомлять своих слушателей о прекращении редактирования)   -  person kleopatra    schedule 17.10.2012
comment
ох... никогда сохраняйте JSomthing в любой модели свинга, вместо этого сохраняйте состояние (здесь логическое значение, не радиокнопка!) и визуализируйте/редактируйте это состояние с помощью соответствующего компонент рендеринга/редактирования.   -  person kleopatra    schedule 17.10.2012
comment
@kleopatra Спасибо, я изменил его на логическое значение (что я сделал ранее). Теперь я получаю ClassCastException (логическое значение нельзя преобразовать в java.awt.Component) при возврате значения (Component); в классе RadioButtonRenderer.   -  person Amarnath    schedule 17.10.2012
comment
обновите код здесь...   -  person kleopatra    schedule 17.10.2012
comment
@kleopatra да, я сделал это. Я использовал логический класс. Теперь проблема в том, что я не могу проверить переключатель.   -  person Amarnath    schedule 17.10.2012


Ответы (3)


Из вашего рисунка видно, что вы хотите обеспечить взаимное исключение между строками JTable, где каждая строка имеет один JRadioButton. Поскольку ButtonGroup не подходит, в этом примере из-за @Guillaume Polet используется собственный менеджер.

person trashgod    schedule 17.10.2012
comment
Я написал новую программу на примере @Guillaume, и она отлично работает. Но все же я не могу понять, в чем проблема с моим старым кодом? ... :( - person Amarnath; 18.10.2012
comment
Проблема в том, что кнопка same используется для рендеринга всех строк, поэтому состояние каждой строки должно храниться в TableModel, и все строки должны обновляться при изменении любой строки. - person trashgod; 18.10.2012
comment
Круто .. ты действительно крут .. 4 дня я пытался и пытался .. наконец, я сделал это. Ваше предложение потрясло. Большое спасибо ..:-) - person Amarnath; 20.10.2012

У меня проблема со следующим кодом. Моя задача: у меня должны быть переключатели в первом столбце, и когда пользователь выбирает этот переключатель, эта строка выбирается и отправляется для обработки. Но моя проблема в том, что я могу выбрать переключатель, который находится в первом столбце, но впоследствии, когда пользователь щелкает в любой части таблицы, мой нажатый переключатель не проверяется. Я не могу понять, почему это происходит. Я действительно застрял с этой проблемой. Требуется помощь. Следующий код показывает мою проблему.

  1. не используйте JRadioButton, используйте встроенную поддержку логического значения в JTable == JCheckBox,

  2. то вы можете sorting и filtering на основе Boolean value

  3. в противном случае вам придется переопределить String ("true" / "false")


  4. есть несколько хороших JRadioButtons как Renderer и Editor в JTable, включая использование JComboBox как Editor для RadioButtonGroup

person mKorbel    schedule 17.10.2012
comment
@trashgod Спасибо за ссылку. Но в приведенном выше коде есть только одна проблема. Если я прокомментирую в super.fireEditingStopped(); в классе RadioButtonEditor. Тогда я не могу нажать на переключатель. Если я раскомментирую эту строку, то если я отмечу переключатель, а затем щелкну в любом месте таблицы, тогда он будет снят. - person Amarnath; 17.10.2012
comment
@Che: я привел рабочий пример здесь. - person trashgod; 17.10.2012

Если вам нужно динамически изменить внешний вид, рекомендуется использовать CellEditor для расширения Component.

//@see javax/swing/SwingUtilities.java
static void updateRendererOrEditorUI(Object rendererOrEditor) {
    if (rendererOrEditor == null) {
        return;
    }
    Component component = null;
    if (rendererOrEditor instanceof Component) {
        component = (Component)rendererOrEditor;
    }
    if (rendererOrEditor instanceof DefaultCellEditor) {
        //Ahh, AbstractCellEditor ...
        component = ((DefaultCellEditor)rendererOrEditor).getComponent();
    }
    if (component != null) {
        SwingUtilities.updateComponentTreeUI(component);
    }
}

Вот пример "CellEditor расширяет JRadioButton...":

import java.awt.*;
import java.awt.event.*;
import java.util.EventObject;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class DisplayTable2 extends JDialog {
  public void initialize() {
    Object[][] data = {
      { true,  "Group1", "Work1" }, { false, "Group2", "Work2" },
      { false, "Group3", "Work3" }, { false, "Group4", "Work4" }
    };
    JTable sourceTable = new JTable(new SourceTableModel(data));
    sourceTable.getColumnModel().getColumn(0).setCellRenderer(new RadioButtonRenderer());
    sourceTable.getColumnModel().getColumn(0).setCellEditor(new RadioButtonEditor());

    JPanel panel = new JPanel();
    panel.add(new JScrollPane(sourceTable));
    add(panel);

    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    setModal(true);
    pack();
    setVisible(true);
  }
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override public void run() {
        new DisplayTable2().initialize();
      }
    });
  }
}

class SourceTableModel extends DefaultTableModel {
  private static final String[] columnNamesList = {"Select", "Group", "Work"};
  public SourceTableModel(Object[][] data) {
    super(data, columnNamesList);
  }
  @Override public Class<?> getColumnClass(int columnIndex) {
    return (columnIndex == 0 ? Boolean.class : String.class);
  }
  @Override public boolean isCellEditable(int rowIndex, int columnIndex) {
    return (columnIndex == 0 ? true : false);
  }
  @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    if(columnIndex==0 && aValue instanceof Boolean) {
      //lazy development
      for(int i=0; i<getRowCount(); i++) {
        super.setValueAt(i==rowIndex, i, columnIndex);
      }
    } else {
      super.setValueAt(aValue, rowIndex, columnIndex);
    }
  }
}

class RadioButtonRenderer extends JRadioButton implements TableCellRenderer {
  @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    if(value instanceof Boolean) {
      setSelected((Boolean)value);
    }
    return this;
  }
}

class RadioButtonEditor extends JRadioButton implements TableCellEditor {
  public RadioButtonEditor() {
    super();
    addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        fireEditingStopped();
      }
    });
  }
  @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    if(value instanceof Boolean) {
      setSelected((Boolean)value);
    }
    return this;
  }
  @Override public Object getCellEditorValue() {
    return isSelected();
  }

  //Copid from AbstractCellEditor
  //protected EventListenerList listenerList = new EventListenerList();
  //transient protected ChangeEvent changeEvent = null;
  @Override public boolean isCellEditable(EventObject e) {
    return true;
  }
  @Override public boolean shouldSelectCell(EventObject anEvent) {
    return true;
  }
  @Override public boolean stopCellEditing() {
    fireEditingStopped();
    return true;
  }
  @Override public void  cancelCellEditing() {
    fireEditingCanceled();
  }
  @Override public void addCellEditorListener(CellEditorListener l) {
    listenerList.add(CellEditorListener.class, l);
  }
  @Override public void removeCellEditorListener(CellEditorListener l) {
    listenerList.remove(CellEditorListener.class, l);
  }
  public CellEditorListener[] getCellEditorListeners() {
    return listenerList.getListeners(CellEditorListener.class);
  }
  protected void fireEditingStopped() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
      }
    }
  }
  protected void fireEditingCanceled() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
      }
    }
  }
}
person aterai    schedule 17.10.2012
comment
нет, это не веская причина для расширения JSomething. Если переключение LAF проблематично, улучшите работу владельца редактора/рендерера. - person kleopatra; 17.10.2012
comment
@kleopatra интересные комментарии по сравнению с ответом одного из гуру Swing, который много раз удивлял других гуру Swing, извините, но я не понимаю (разве мы не говорим о SwingX ???), Renderer и Editor должны что-то расширять, включая уведомители, а затем UIDelegate, конечно для компонентов JButtons, там я вижу, что все уведомители и UIDelegate будут правильно реализованы, особенно часть из них чувствительна к L&F... - person mKorbel; 17.10.2012
comment
@kleopatra Спасибо за предложение. Я предполагаю, что улучшение владельца редактора может заключаться в том, что значение переопределяет JTable#updateUI(), верно? (но мне не легко) - person aterai; 17.10.2012
comment
@mKorbel вам нужен JSomething в качестве компонента редактирования/рендеринга, а не JSomething в качестве редактора/рендерера, последние являются только поставщиками соответствующих компонентов. Плохой стиль - расширять первое, чтобы играть роль второго, даже если это делает основной поворот (что приводит к иногда катастрофическому результату как в DefaultTableCellRenderer) - person kleopatra; 17.10.2012
comment
да, точно :-) Один из вариантов (как это сделано в SwingX) состоит в том, чтобы иметь интерфейс UIDependent (в основном только один метод updateUI), позволить пользовательским средствам визуализации реализовать его и улучшить updateUI представлений коллекции, чтобы проверить средства визуализации, реализующие этот интерфейс. Или используйте SwingX для начала :-) - person kleopatra; 17.10.2012
comment
аааааа спасибо за ...., мой чиееееее щенок гоняется за собственным хвостом :-), согласен но тогда нам придется (лучше будет) защищаться использовать JButtons Editor в JTable ....., - person mKorbel; 17.10.2012
comment
@aterai посмотрите и здесь, (Nimbus) от @aephyr - person mKorbel; 17.10.2012
comment
@mKorbel Спасибо за ссылку. Я буду смотреть в него. - person aterai; 17.10.2012