3

I have a problem with the following code. My task is, I have to have radio buttons in the first column and when a user selects that radio button that row is selected and sent for processing. But my problem is, I am able to select the radio button which are in the first column, but afterwards when user clicks in any part of the table then my clicked radio button is being unchecked. I am not able to figure, why it is happeneing. I am really stuck with this problem. Help required. The following code shows my problem.

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: I have updated my code and I have used Boolean class for the first column. 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. If I keep the super.fireEditingStopped(); then I am not even able to check the Radio button.

I know that super.fireEditingStopped(); will stop editing. But my question is how to check it?

P.S: Sorry I have posted my entire code. I thought it will be easy for some one to look at the problem.

This is the screen shot of the program. enter image description here

Amarnath
  • 8,736
  • 10
  • 54
  • 81
  • (probably, to hard to read that suboptimally formatted code) unrelated to your problem: the editor implemenation is _invalid_ (it must notify its listeners on terminating edits) – kleopatra Oct 17 '12 at 09:31
  • 5
    ohh .. _never-ever_ store JSomthing in any swing model, instead store the state (here a boolean, **not** a radiobutton!) and render/edit that state with an appropriate renderering/editing component. – kleopatra Oct 17 '12 at 09:33
  • @kleopatra Thanks I have changed it to boolean (which I have done previously.) Now I am getting a ClassCastException (Boolean cannot be cast to java.awt.Component) at "return (Component) value;" in RadioButtonRenderer class. – Amarnath Oct 17 '12 at 09:57
  • update the code here ... – kleopatra Oct 17 '12 at 10:11
  • @kleopatra yes I have done that. I used Boolean class. Now problem is I am not able to check the radio button. – Amarnath Oct 17 '12 at 10:39

3 Answers3

3

From your illustration, it appears that you want to enforce mutual exclusion among the rows of a JTable, where each row has a single JRadioButton. As a ButtonGroup is unsuitable, this example due to @Guillaume Polet uses a custom manager.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I wrote new program using @Guillaume example and it is working great. But still I am not able to figure it out what is the problem with my old code? ... :( – Amarnath Oct 18 '12 at 01:48
  • The problem is that the _same_ button is used for rendering all rows, so the state for each row must be stored in the `TableModel`, and all rows must be updated when any row is changed. – trashgod Oct 18 '12 at 14:10
  • Awesome .. u are r really awesome .. from 4 days I was trying and trying .. finally I made it. Your suggestion rocked. Thanks a lot ..:-) – Amarnath Oct 20 '12 at 12:48
2

I have a problem with the following code. My task is, I have to have radio buttons in the first column and when a user selects that radio button that row is selected and sent for processing. But my problem is, I am able to select the radio button which are in the first column, but afterwards when user clicks in any part of the table then my clicked radio button is being unchecked. I am not able to figure, why it is happeneing. I am really stuck with this problem. Help required. The following code shows my problem.

  1. don't to use JRadioButton, use built_in support for Boolean value in JTable == JCheckBox,

  2. then you can sorting and filtering based on Boolean value

  3. otherwise you have to override to String ("true" / "false")


  4. there are a few good JRadioButtons as Renderer and Editor in JTable, including usage of JComboBox as Editor for RadioButtonGroup

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • @trashgod Thanks for the reference. But there is only one problem in the above code. If I comment in super.fireEditingStopped(); in RadioButtonEditor class. Then I am not able to click on the radio button. If I uncomment that line then if I check a radio button and then click anywhere on the table then it is unchecking. – Amarnath Oct 17 '12 at 11:08
  • @Che: I've cited a working example [here](http://stackoverflow.com/posts/12933239/edit). – trashgod Oct 17 '12 at 11:25
1

If you need to dynamically change the Look and Feel, your CellEditor is recommended to extend 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);
    }
}

Here is a "CellEditor extends JRadioButton ..." example:

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);
      }
    }
  }
}
aterai
  • 9,658
  • 4
  • 35
  • 44
  • no, not a valid reason for extending a JSomething. If LAF toggling is problematic, enhance the editor/renderer owner to do better – kleopatra Oct 17 '12 at 12:31
  • @kleopatra interesting comments in compare with answer by one of Swing Guru that suprised another Swing Gurus many times, sorry but I don't understand, (aren't we talking about SwingX???), Renderer and Editor must to extends something inc notifiers and then UIDelegate, sure for JButtons Components, there I see all notifiers and UIDelegate will be correctly implemeted, especially part of them is L&F sensitive ... – mKorbel Oct 17 '12 at 13:20
  • @kleopatra Thank you for the suggestion. I guess "enhance the editor owner" may be that the meaning override JTable#updateUI(), right? (but it's not easy for me) – aterai Oct 17 '12 at 13:26
  • @mKorbel you need a JSomething as the editing/rendering _component_ vs. a JSomething as _editor/renderer_ the latter are only the providers of the appropriate components. It's bad style to extend the former to play the role of the latter, even though core swing does it (leading to the [occasional catastrophic outcome](http://stackoverflow.com/a/7672836/203657) as in DefaultTableCellRenderer) – kleopatra Oct 17 '12 at 13:30
  • 1
    yeah, exactly :-) One option (as done in SwingX) is to have an interface UIDependent (basically just one method updateUI), let the custom renderers implement it and enhance the collection views' updateUI to check for renderers implementing that interface. Or use SwingX to start with :-) – kleopatra Oct 17 '12 at 13:33
  • aaaach thank you for ...., my cheeeeeer puppy chasing its own tail :-), agreed but then we have to (better will be) protecting to use JButtons Editor in the JTable ....., – mKorbel Oct 17 '12 at 13:38
  • 1
    @aterai [look here too, (Nimbus) by @aephyr](http://aephyr.googlecode.com/svn/trunk/) – mKorbel Oct 17 '12 at 13:41