Showing posts with label swing. Show all posts
Showing posts with label swing. Show all posts

Friday, August 25, 2006

Custom Cell Renderers and Custom Cell Editors in Swing - Part III

Now lets take a look at how the CustomJTable code looks like. In order to allow external controllers to specify which column of the JTable will render itself as JRadioButtons, we have a method called setRadioGroup.  We also declare the custom Table Model which hides our internal structure and works the Model magic of Swing by extending AbstractTableModel.

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;

import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

public class CustomJTable extends JTable {

  /**
   * Give support for Grouping Radio Buttons in
   * different rows of a specific column in the table
   <br>
   * Optionally also allow a CellRendererValidator
   * which can choose which rows in the table will
   * have the radio button visible
   
   @param columnIndex -
   *            The column to which the grouping must
   *            be enabled
   
   @param validator -
   *            An optional CellRendererValidator to
   *            choose which columns have visible
   *            radio buttons. By default all column
   *            will show the radiobutton
   
   @see CellRendererValidator
   */
  public void setRadioGroup(int columnIndex,
      CellRendererValidator validator) {
    getColumnModel().getColumn(columnIndex)
        .setCellEditor(
            new RadioColumnCellEditor());
    getColumnModel().getColumn(columnIndex)
        .setCellRenderer(
            new RadioColumnRenderer(
                validator));
  }

  public boolean isCellEditable(int rowIndex,
      int vColIndex) {
    return true;
  }

  public Class getColumnClass(int column) {
    Object value = getValueAt(0, column);
    if (value != null)
      return value.getClass();
    else
      return super.getColumnClass(column);
  }

  /**
   * CellRendererValidator - This is an interface
   * that will be implemented by any class that
   * wishes <br>
   * to specify whether a CellRenderer is valid for a
   * specific column and row. <br>
   * This is pretty useful when specific rows of a
   * column will <i>NOT </i> the renderer set for the
   * entire column
   
   @author rajeshv
   */
  public static interface CellRendererValidator {
    public boolean isValid(JTable table,
        int rowIndex, int colIndex);
  }

  public static class CustomJTableModel extends
      AbstractTableModel {
    private List dataList = null;

    private String[] properties = null;

    public CustomJTableModel(List list) {
      dataList = list;
      LinkedHashMap map =
          (LinkedHashMapdataList.get(0);
      Set keySet = map.keySet();
      properties = new String[keySet.size()];
      int index = 0;
      for (Iterator iter = keySet.iterator(); iter
          .hasNext();) {
        properties[index++=
            (Stringiter.next();
      }
    }

    public int getColumnCount() {
      LinkedHashMap table =
          (LinkedHashMapdataList.get(0);
      return table.size();
    }

    public Object getValueAt(int rowIndex,
        int columnIndex) {
      LinkedHashMap table =
          (LinkedHashMapdataList
              .get(rowIndex);
      return table.get(properties[columnIndex]);
    }

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

    public String getColumnName(int columnIndex) {
      return properties[columnIndex];
    }

    public Class getColumnClass(int columnIndex) {
      return String.class;
    }

    public void setValueAt(Object aValue,
        int rowIndex, int columnIndex) {
      LinkedHashMap table =
          (LinkedHashMapdataList
              .get(rowIndex);
      table.put(properties[columnIndex], aValue);
      fireTableCellUpdated(rowIndex, columnIndex);
    }

    public boolean isCellEditable(int rowIndex,
        int columnIndex) {
      return false;
    }
  }

  public static void main(String[] args) {
    CustomJTable table = new CustomJTable();
    LinkedHashMap firstRow = new LinkedHashMap();
    firstRow.put("column1""test1");
    firstRow.put("column2", Boolean.TRUE);
    LinkedHashMap secondRow = new LinkedHashMap();
    secondRow.put("column1""test1");
    secondRow.put("column2", Boolean.FALSE);
    LinkedHashMap thirdRow = new LinkedHashMap();
    thirdRow.put("column1""test1");
    thirdRow.put("column2""test");

    List dataList = new ArrayList();
    dataList.add(firstRow);
    dataList.add(secondRow);
    dataList.add(thirdRow);
    table
        .setModel(new CustomJTable.CustomJTableModel(
            dataList));
    table
        .setRadioGroup(
            1,
            new CustomJTable.CellRendererValidator(){
              public boolean isValid(
                  JTable table,
                  int rowIndex,
                  int colIndex) {
                if (rowIndex == 2) {
                  return false;
                }
                return true;
            });
    JFrame frame = new JFrame();
    frame
        .setDefaultCloseOperation(
                        
JFrame.EXIT_ON_CLOSE);
    frame.setSize(600600);
    frame.getContentPane().add(table);
    frame.setVisible(true);
  }
}

Thursday, August 24, 2006

Custom Cell Renderers and Custom Cell Editors in Swing - Part II


RadioColumnEditor
:
 The Editor is pretty much standard and the method to note is getTableCellEditorComponent() which contains the core logic.



import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;

import javax.swing.JRadioButton;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;

public class RadioColumnCellEditor implements
    ActionListener, TableCellEditor {
  Color currentColor;

  JRadioButton button;

  boolean state;

  public RadioColumnCellEditor() {
    super();
    button = new JRadioButton();
    button.setOpaque(false);
    button.setBackground(Color.WHITE);
    button.addActionListener(this);
  }

  protected EventListenerList listenerList =
      new EventListenerList();

  protected Object value;

  protected ChangeEvent changeEvent = null;

  protected int clickCountToStart = 1;

  public void setCellEditorValue(Object value) {
    this.value = value;
  }

  /**
   * Add a listener to the list that's notified when
   * the editor starts, stops, or cancels editing.
   */
  public void addCellEditorListener(
      CellEditorListener l) {
    listenerList.add(CellEditorListener.class, l);
  }

  /**
   * Remove a listener from the list that's notified
   */
  public void removeCellEditorListener(
      CellEditorListener l) {
    listenerList.remove(CellEditorListener.class,
        l);
  }

  protected void fireEditingStopped() {
    Object[] listeners =
        listenerList.getListenerList();
    for (int i = listeners.length - 2; i >= 0; i -=
        2) {
      if (listeners[i== CellEditorListener.class) {
        if (changeEvent == null)
          changeEvent =
              new ChangeEvent(this);
        ((CellEditorListenerlisteners[i + 1])
            .editingStopped(changeEvent);
      }
    }
  }

  protected void fireEditingCanceled() {
    Object[] listeners =
        listenerList.getListenerList();
    for (int i = listeners.length - 2; i >= 0; i -=
        2) {
      if (listeners[i== CellEditorListener.class) {
        if (changeEvent == null)
          changeEvent =
              new ChangeEvent(this);
        ((CellEditorListenerlisteners[i + 1])
            .editingCanceled(changeEvent);
      }
    }
  }

  public void actionPerformed(ActionEvent event) {
    fireEditingStopped();
  }

  // Implement the one CellEditor method that
  // AbstractCellEditor doesn't.
  public Object getCellEditorValue() {
    return new Boolean(button.isSelected());
  }

  // Implement the one method defined by
  // TableCellEditor.
  public Component getTableCellEditorComponent(
      JTable table, Object value,
      boolean isSelected, int row, int column) {

    if (!value.getClass().getName().equals(
        Boolean.class.getName())) {
      return null;
    }
    button.setSelected(value != null
        && value.getClass().getName().equals(
            Boolean.class.getName())
        && ((Booleanvalue).booleanValue());
    TableModel model = table.getModel();
    // Make all the radioButtons, except the
    // selected
    // TRUE
    for (int i = 0; i < model.getRowCount(); i++) {
      if (model.getValueAt(i, column!= null
          && model
              .getValueAt(i, column)
              .getClass()
              .getName()
              .equals(
                  Boolean.class
                      .getName())) {
        model.setValueAt(Boolean.FALSE, i,
            column);
      }
    }
    model.setValueAt(Boolean.TRUE, row, column);
    button
        .setHorizontalAlignment(
                              
SwingConstants.CENTER);
    return button;
  }

  public boolean stopCellEditing() {
    fireEditingStopped();
    return true;
  }

  public void cancelCellEditing() {
    fireEditingCanceled();
  }

  public boolean shouldSelectCell(EventObject eo) {
    return true;
  }

  public boolean isCellEditable(EventObject eo) {
    return true;
  }
}

Custom Cell Renderers and Custom Cell Editors in Swing - Part I

JTable provides the ability for individual columns to have different renderers and editors. This is very powerful functionality because you can have a Radio Button in one of the columns or a JButton or JComboBox in some columns.


I went ahead a wrote a small JTable Component that displays a Radio Button in one of its columns. I also customized the component to display the radio button only for specific rows in the column and also added functionality to behave as a radio button group!


The JTable that I wrote has the ability for some external controller to specify
which rows of a particular column must render its value as RadioButtons.
I achieved this by writing an interface with a single method which provided
any renderer with the ability to check if the current row, column can be
rendered by itself.


  /**
   * CellRendererValidator - This is an interface 
   * that will be implemented by any class that 
   wishes <br>to specify whether a CellRenderer 
   * is valid for a specific column and row. <br>
   * This is pretty useful when specific rows of a 
   * column will <i>NOT </i> the renderer set for 
   * the entire column
   
   @author rajeshv
   */
  public static interface CellRendererValidator {
    public boolean isValid(JTable table, 
                          int rowIndex,int colIndex);
  }

I also wrote a TableCellRenderer and TableCellEditor for the RadioButton Column. Here the code for Both.


RadioColumnRenderer:



import java.awt.Color;
import java.awt.Component;

import javax.swing.JRadioButton;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.TableCellRenderer;

public class RadioColumnRenderer extends JRadioButton
    implements TableCellRenderer {
  private CustomJTable.CellRendererValidator 
                                    validator = 
null;

  /**
   
   */
  public RadioColumnRenderer(
      CustomJTable.CellRendererValidator validator) {
    this.validator = validator;
  }

  public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected,
      boolean hasFocus, int row, int column) {
    // this is necessary because we may have 
    // values that are not boolean for
    // som of the columns
    this.setSelected(value != null
        && value.getClass().getName().equals(
            Boolean.class.getName())
        && ((Booleanvalue).booleanValue());

    this.setHorizontalAlignment(
                         
SwingConstants.CENTER);
    this.setOpaque(false);
    this.setBackground(Color.WHITE);
    // request the validator to concur that this 
    // column can be rendered as a JRadioButton
    if (validator != null) {
      if (validator.isValid(table, row, column)) {
        return this;
      }
      return null;
    }
    return this;
  }
}