An "expandable" list is a list which has a bigger selected item. A good example is the add-ons list in Firefox (Tools / Add-ons) :
With this kind of list :
- the unselected item are "read only" (just display some informations)
- only the selected item can be "edited" (buttons only appears for the selected line)
You can also use this as a "Fisheye" list.
I was thinking that it would be pretty simple to do this with Swing, and I was wrong. So here my little component, which is basically a JTable that you use like a JList :
import java.awt.Component;
import java.awt.Dimension;
import java.util.EventObject;
import java.util.Vector;
import javax.swing.AbstractCellEditor;
import javax.swing.AbstractListModel;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
/**
* A list with the selected item bigger than the others
* @author Christophe Le besnerais
* @link http://swing-fx.blogspot.com/
*/
@SuppressWarnings("serial")
public class JExpandList extends JTable {
private int selectedCellHeight = 50, unselectedCellHeight = 20;
/**
* Constructs a JExpandList that displays the elements in the
* specified, non-null model. All JList
* constructors delegate to this one.
*
* @param dataModel the data model for this list
* @exception IllegalArgumentException if dataModel is null
*/
public JExpandList(ListModel dataModel) {
super(getModel(dataModel));
// remove unused stuffs from JTable :
this.setTableHeader(null);
this.setShowGrid(false);
this.setIntercellSpacing(new Dimension(0, 0));
this.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
private static DefaultTableModel getModel(ListModel dataModel) {
// convert from ListModel to TableModel :
DefaultTableModel model = new DefaultTableModel();
Object[] data = new Object[dataModel.getSize()];
for (int i = 0; i < dataModel.getSize(); i++) {
data[i] = dataModel.getElementAt(i);
}
model.addColumn("", data);
return model;
}
/**
* Constructs a JExpandList that displays the elements in the
* specified array. This constructor just delegates to the
* ListModel constructor.
*
* @param listData the array of Objects to be loaded into the data model
*/
public JExpandList(final Object[] listData) {
this(new AbstractListModel() {
public int getSize() {
return listData.length;
}
public Object getElementAt(int i) {
return listData[i];
}
});
}
/**
* Constructs a JExpandList that displays the elements in the
* specified Vector. This constructor just delegates to the
* ListModel constructor.
*
* @param listData the Vector to be loaded into the data model
*/
public JExpandList(final Vector> listData) {
this(new AbstractListModel() {
public int getSize() {
return listData.size();
}
public Object getElementAt(int i) {
return listData.elementAt(i);
}
});
}
/**
* Constructs a JExpandList with an empty model.
*/
public JExpandList() {
this(new AbstractListModel() {
public int getSize() {
return 0;
}
public Object getElementAt(int i) {
return "No Data Model";
}
});
}
@Override
public boolean editCellAt(int row, int column, EventObject e) {
// "one click" edit :
selectionModel.setSelectionInterval(row, row);
return super.editCellAt(row, column, e);
}
/**
* @return the height of the selected item
*/
public int getSelectedCellHeight() {
return selectedCellHeight;
}
/**
* set the heigth of the selected item
*/
public void setSelectedCellHeight(int selectedCellHeight) {
this.selectedCellHeight = selectedCellHeight;
}
/**
* @return the height of an unselected item
*/
public int getUnselectedCellHeight() {
return unselectedCellHeight;
}
/**
* set the height of an unselected item
*/
public void setUnselectedCellHeight(int unselectedCellHeight) {
this.unselectedCellHeight = unselectedCellHeight;
}
/**
* Sets the delegate that's used to paint each cell in the list.
* The height of the cell will be getUnselectedCellHeight or
* getSelectedCellHeight (if the cell is selected).
* @param cellRenderer the ListCellRenderer that paints list cells
*/
public void setCellRenderer(TableCellRenderer cellRenderer) {
this.setDefaultRenderer(Object.class, new JExpandListCellRenderer(cellRenderer));
this.setDefaultEditor(Object.class, new JExpandListCellRenderer(cellRenderer));
}
private class JExpandListCellRenderer extends AbstractCellEditor implements TableCellRenderer, TableCellEditor {
private Object value;
private TableCellRenderer cellRenderer;
public JExpandListCellRenderer(TableCellRenderer cellRenderer) {
this.cellRenderer = cellRenderer;
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
// change the size of the current cell (selected cell are bigger) :
int height = isSelected ? selectedCellHeight : unselectedCellHeight;
if (table.getRowHeight(row) != height)
table.setRowHeight(row, height);
return cellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = value;
return this.getTableCellRendererComponent(table, value, isSelected, isSelected, row, column);
}
public Object getCellEditorValue() {
return value;
}
@Override
public boolean isCellEditable(EventObject e) {
return true;
}
}
}
And how to use it :
Object[] data = {"Test 1", Boolean.TRUE, "Test 2", new Integer(5)};
JExpandList list = new JExpandList(data);
list.setCellRenderer(new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = new JLabel(value.toString());
label.setOpaque(true);
if (isSelected)
label.setBackground(Color.BLUE);
return label;
}
});
You can set the height of the items with setUnselectedCellHeight and setSelectedCellHeight. The isSelected parameter from your renderer tell you if the item is the selected one, so you can add some buttons to your component if its value is true.


0 comments:
Post a Comment