I have a problem with JComboBoxes and long Strings. The result is that the JComboBox is too long for the JFrame so it doesn't display the whole box.
Here i have a little code which shows my problem:
JFrame frame = new JFrame();
frame.setSize(500, 500);
JPanel panel = new JPanel();
DesignGridLayout layout = new DesignGridLayout(panel);
Vector<ArrayList<String>> content = new Vector<ArrayList<String>>();
ArrayList<String> list = new ArrayList<String>();
list.add("12345");
list.add("abcde");
list.add("12345");
list.add("abcde");
list.add("12345");
list.add("abcde");
list.add("12345");
list.add("abcde");
list.add("12345");
list.add("abcde");
list.add("12345");
list.add("abcde");
content.add(list);
ComboBoxFullMenu<ArrayList<String>> combobox = new ComboBoxFullMenu<ArrayList<String>>(content);
combobox.setRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 1L;
#Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
ArrayList<String> array = (ArrayList<String>) value;
if (!array.isEmpty())
{
String text = "";
for (String info : array)
{
text += info + "; ";
}
setText(text);
}
return this;
}
});
AutoCompleteDecorator.decorate(combobox, new ObjectToStringConverter()
{
#Override
public String getPreferredStringForItem(Object object)
{
if (object != null)
{
if (object instanceof ArrayList)
{
ArrayList<String> list = (ArrayList<String>) object;
String text = "";
for (String info : list)
{
text += info + "; ";
}
return text;
}
else
{
return object.toString();
}
}
return null;
}
});
layout.row().grid().add(combobox);
frame.add(panel);
frame.setVisible(true);
And here is the class ComboBoxFullMenu:
public class ComboBoxFullMenu<E> extends JComboBox<E>
{
/**
*
*/
private static final long serialVersionUID = 1L;
public ComboBoxFullMenu(Vector<E> items)
{
super(items);
addActionListener(this);
}
public ComboBoxFullMenu()
{
super();
addActionListener(this);
}
public ComboBoxFullMenu (DefaultComboBoxModel<E> defaultComboBoxModel)
{
super(defaultComboBoxModel);
addActionListener(this);
}
/**
* Small hack to get pop up menu size bigger enough to show items even though
* the combo box size could be smaller
* */
private boolean layingOut = false;
#Override
public void doLayout()
{
try
{
layingOut = true;
super.doLayout();
}
finally
{
layingOut = false;
}
}
#Override
public Dimension getSize()
{
Dimension dim = super.getSize();
if ( !layingOut )
{
dim.width = Math.max(dim.width, getPreferredSize().width);
}
return dim;
}
}
I also tried using DefaultListCellRenderer and ObjectTroStringConverter to display the selected item in a shorter way, but i need to see all informations in the dropdownmenu and i think the JComboBox calculates its width about the longest item?
I hope you understand my problem otherwise let me know.
Check out Combo Box Popup is allows you to control the width of the popup. You can set the width
to the size of the largest string, or
specify a maximum width.
You can then turn on scrolling in the popup if the string is larger than the specified width.
Try using this WideComboBox. It allows you to set the width of the JComboBox but when the popup is show it shows the entire item. Meaning the popup will be wider than the JComboBox. You also may find this helpful
Related
In fact i know how to implement using CTRL+Z (Undo) and CTRL+Y (Redo) with one JTextField. But i have hundreds of Text Components in my Swing application, so is there a way to apply this for all Text Components in my application, so when i click CTRL+Z in any Text Component it would undo the last entry in that Field ?
I have tried to implement it in EventQueue, but it did not work !
To make all your text components "undoable", you can simply create them using your own subclass like:
public class MyTextField extends JTextField {
public MyTextField() {
final UndoManager undoMgr = new UndoManager();
// Add listener for undoable events
getDocument().addUndoableEditListener(new UndoableEditListener() {
public void undoableEditHappened(UndoableEditEvent pEvt) {
undoMgr.addEdit(pEvt.getEdit());
}
});
// Add undo/redo actions
getActionMap().put(UNDO_ACTION, new AbstractAction(UNDO_ACTION) {
public void actionPerformed(ActionEvent pEvt) {
try {
if (undoMgr.canUndo()) {
undoMgr.undo();
}
} catch (CannotUndoException e) {
e.printStackTrace();
}
}
});
getActionMap().put(REDO_ACTION, new AbstractAction(REDO_ACTION) {
public void actionPerformed(ActionEvent pEvt) {
try {
if (undoMgr.canRedo()) {
undoMgr.redo();
}
} catch (CannotRedoException e) {
e.printStackTrace();
}
}
});
// Create keyboard accelerators for undo/redo actions (Ctrl+Z/Ctrl+Y)
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK),
UNDO_ACTION);
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK),
REDO_ACTION);
}
}
Then instead of creating JTextFields, create MyTextFields. The limitation is that you may also want to create another subclass for JTextArea and so on for other JTextComponents. So it is also possible to use an utility method to add Undo/Redo features to any existing JTextCompoent:
public final static String UNDO_ACTION = "Undo";
public final static String REDO_ACTION = "Redo";
public static void makeUndoable(JTextComponent pTextComponent) {
final UndoManager undoMgr = new UndoManager();
// Add listener for undoable events
pTextComponent.getDocument().addUndoableEditListener(new UndoableEditListener() {
public void undoableEditHappened(UndoableEditEvent evt) {
undoMgr.addEdit(evt.getEdit());
}
});
// Add undo/redo actions
pTextComponent.getActionMap().put(UNDO_ACTION, new AbstractAction(UNDO_ACTION) {
public void actionPerformed(ActionEvent evt) {
try {
if (undoMgr.canUndo()) {
undoMgr.undo();
}
} catch (CannotUndoException e) {
e.printStackTrace();
}
}
});
pTextComponent.getActionMap().put(REDO_ACTION, new AbstractAction(REDO_ACTION) {
public void actionPerformed(ActionEvent evt) {
try {
if (undoMgr.canRedo()) {
undoMgr.redo();
}
} catch (CannotRedoException e) {
e.printStackTrace();
}
}
});
// Create keyboard accelerators for undo/redo actions (Ctrl+Z/Ctrl+Y)
pTextComponent.getInputMap().put(
KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK), UNDO_ACTION);
pTextComponent.getInputMap().put(
KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK), REDO_ACTION);
}
The final and absolutely transparent solution would be to implement your own UI class using Lnf features, but you may want to think twice before making all TextComponents undoable, for memory consumptions reasons for example if you often perform huge text modifications to these components...
you can gel list of built_in KeyBindings short_cuts implemented in the API's
notice you have to check or prepare your code for all accesible Look and Feels
you can get built_in KeyBindings short_cuts and replace that as you expecting, System ClipBoard is very often modified,
never tried but on this forum you can foud out rellevant info How to change, replace KeyBindings short_cuts
list of built_in KeyBindings short_cuts
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import javax.swing.filechooser.*;
public class KeyBindings implements ItemListener {
private static final String PACKAGE = "javax.swing.";
private static final String[] COLUMN_NAMES = {"Action", "When Focused", "When In Focused Window", "When Ancestor"};
private static String selectedItem;
private JComponent contentPane;
private JMenuBar menuBar;
private JTable table;
private JComboBox comboBox;
private Hashtable<String, DefaultTableModel> models;
/*
* Constructor
*/
public KeyBindings() {
models = new Hashtable<String, DefaultTableModel>();
contentPane = new JPanel(new BorderLayout());
contentPane.add(buildNorthComponent(), BorderLayout.NORTH);
contentPane.add(buildCenterComponent(), BorderLayout.CENTER);
resetComponents();
}
/*
* The content pane should be added to a high level container
*/
public JComponent getContentPane() {
return contentPane;
}
/*
* A menu can also be added which provides the ability to switch
* between different LAF's.
*/
public JMenuBar getMenuBar() {
if (menuBar == null) {
menuBar = createMenuBar();
}
return menuBar;
}
/*
* This panel is added to the North of the content pane
*/
private JComponent buildNorthComponent() {
comboBox = new JComboBox();
JLabel label = new JLabel("Select Component:");
label.setDisplayedMnemonic('S');
label.setLabelFor(comboBox);
JPanel panel = new JPanel();
panel.setBorder(new EmptyBorder(15, 0, 15, 0));
panel.add(label);
panel.add(comboBox);
return panel;
}
/*
* Check the key name to see if it is the UI property
*/
private String checkForUIKey(String key) {
if (key.endsWith("UI") && key.indexOf('.') == -1) {
String componentName = key.substring(0, key.length() - 2);// Ignore these components
if (componentName.equals("PopupMenuSeparator") || componentName.equals("ToolBarSeparator") || componentName.equals("DesktopIcon")) {
return null;
} else {
return componentName;
}
}
return null;
}
/*
** Build the emtpy table to be added in the Center
*/
private JComponent buildCenterComponent() {
DefaultTableModel model = new DefaultTableModel(COLUMN_NAMES, 0);
table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
table.setAutoCreateColumnsFromModel(false);
table.getColumnModel().getColumn(0).setPreferredWidth(200);
table.getColumnModel().getColumn(1).setPreferredWidth(200);
table.getColumnModel().getColumn(2).setPreferredWidth(200);
table.getColumnModel().getColumn(3).setPreferredWidth(200);
Dimension d = table.getPreferredSize();
d.height = 350;
table.setPreferredScrollableViewportSize(d);
table.getTableHeader().setFocusable(true);
return new JScrollPane(table);
}
/*
* When the LAF is changed we need to reset all the items
*/
private void resetComponents() {
models.clear();
((DefaultTableModel) table.getModel()).setRowCount(0);
Vector<String> comboBoxItems = new Vector<String>(50);// buildItemsMap();
UIDefaults defaults = UIManager.getLookAndFeelDefaults();
for (Object key : defaults.keySet()) { // All Swing components will have a UI property.
String componentName = checkForUIKey(key.toString());
if (componentName != null) {
comboBoxItems.add(componentName);
}
}
Collections.sort(comboBoxItems);
comboBox.removeItemListener(this);
comboBox.setModel(new DefaultComboBoxModel(comboBoxItems));
comboBox.setSelectedIndex(-1);
comboBox.addItemListener(this);
comboBox.requestFocusInWindow();
if (selectedItem != null) {
comboBox.setSelectedItem(selectedItem);
}
}
/**
* Create menu bar
*/
private JMenuBar createMenuBar() {
JMenuBar menuBar1 = new JMenuBar();
menuBar1.add(createFileMenu());
menuBar1.add(createLAFMenu());
return menuBar1;
}
/**
* Create menu items for the Application menu
*/
private JMenu createFileMenu() {
JMenu menu = new JMenu("Application");
menu.setMnemonic('A');
menu.addSeparator();
menu.add(new ExitAction());
return menu;
}
/**
* Create menu items for the Look & Feel menu
*/
private JMenu createLAFMenu() {
ButtonGroup bg = new ButtonGroup();
JMenu menu = new JMenu("Look & Feel");
menu.setMnemonic('L');
String lafId = UIManager.getLookAndFeel().getID();
UIManager.LookAndFeelInfo[] lafInfo = UIManager.getInstalledLookAndFeels();
for (int i = 0; i < lafInfo.length; i++) {
String laf = lafInfo[i].getClassName();
String name = lafInfo[i].getName();
Action action = new ChangeLookAndFeelAction(laf, name);
JRadioButtonMenuItem mi = new JRadioButtonMenuItem(action);
menu.add(mi);
bg.add(mi);
if (name.equals(lafId)) {
mi.setSelected(true);
}
}
return menu;
}
/*
* Implement the ItemListener interface
*/
#Override
public void itemStateChanged(ItemEvent e) {
String componentName = (String) e.getItem();
changeTableModel(getClassName(componentName));
selectedItem = componentName;
}
/*
* Use the component name to build the class name
*/
private String getClassName(String componentName) {
if (componentName.equals("TableHeader")) {// The table header is in a child package
return PACKAGE + "table.JTableHeader";
} else {
return PACKAGE + "J" + componentName;
}
}
/*
* Change the TabelModel in the table for the selected component
*/
private void changeTableModel(String className) {
DefaultTableModel model = models.get(className); // Check if we have already built the table model for this component
if (model != null) {
table.setModel(model);
return;
}
model = new DefaultTableModel(COLUMN_NAMES, 0); // Create an empty table to start with
table.setModel(model);
models.put(className, model);
JComponent component = null; // Create an instance of the component so we can get the default Action map and Input maps
try {
if (className.endsWith("JFileChooser")) {// Hack so I don't have to sign the jar file for usage in Java Webstart
component = new JFileChooser(new DummyFileSystemView());
} else {
Object o = Class.forName(className).newInstance();
component = (JComponent) o;
}
} catch (Exception e) {
Object[] row = {e.toString(), "", "", ""};
model.addRow(row);
return;
}
ActionMap actionMap = component.getActionMap(); // Not all components have Actions defined
Object[] keys = actionMap.allKeys();
if (keys == null) {
Object[] row = {"No actions found", "", "", ""};
model.addRow(row);
return;
}
// In some ActionMaps a key of type Object is found (I have no idea why)
// which causes a ClassCastExcption when sorting so we will ignore it
// by converting that entry to the empty string
for (int i = 0; i < keys.length; i++) {
Object key = keys[i];
if (key instanceof String) {
continue;
} else {
keys[i] = "";
}
}
Arrays.sort(keys);
for (int i = 0; i < keys.length; i++) { // Create a new row in the model for every Action found
Object key = keys[i];
if (key != "") {
Object[] row = {key, "", "", ""};
model.addRow(row);
}
}
// Now check each InputMap to see if a KeyStroke is bound the the Action
updateModelForInputMap(model, 1, component.getInputMap(JComponent.WHEN_FOCUSED));
updateModelForInputMap(model, 2, component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW));
updateModelForInputMap(model, 3, component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
}
/*
* The model is potentially update for each of the 3 different InputMaps
*/
private void updateModelForInputMap(TableModel model, int column, InputMap inputMap) {
if (inputMap == null) {
return;
}
KeyStroke[] keys = inputMap.allKeys();
if (keys == null) {
return;
}
// The InputMap is keyed by KeyStroke, however we want to be able to
// access the action names that are bound to a KeyStroke so we will create
// a Hashtble that is keyed by action name.
// Note that multiple KeyStrokes can be bound to the same action name.
Hashtable<Object, String> actions = new Hashtable<Object, String>(keys.length);
for (int i = 0; i < keys.length; i++) {
KeyStroke key = keys[i];
Object actionName = inputMap.get(key);
Object value = actions.get(actionName);
if (value == null) {
actions.put(actionName, key.toString().replace("pressed ", ""));
} else {
actions.put(actionName, value + ", " + key.toString().replace("pressed ", ""));
}
}
for (int i = 0; i < model.getRowCount(); i++) {
// Now we can update the model for those actions that have KeyStrokes mapped to them
String o = actions.get(model.getValueAt(i, 0));
if (o != null) {
model.setValueAt(o.toString(), i, column);
}
}
}
/*
* Change the LAF and recreate the UIManagerDefaults so that the properties
* of the new LAF are correctly displayed.
*/
private class ChangeLookAndFeelAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private String laf;
protected ChangeLookAndFeelAction(String laf, String name) {
this.laf = laf;
putValue(Action.NAME, name);
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
}
#Override
public void actionPerformed(ActionEvent e) {
try {
JMenuItem mi = (JMenuItem) e.getSource();
JPopupMenu popup = (JPopupMenu) mi.getParent();
JRootPane rootPane = SwingUtilities.getRootPane(popup.getInvoker());
Component c = rootPane.getContentPane().getComponent(0);
rootPane.getContentPane().remove(c);
UIManager.setLookAndFeel(laf);
KeyBindings bindings = new KeyBindings();
rootPane.getContentPane().add(bindings.getContentPane());
SwingUtilities.updateComponentTreeUI(rootPane);
rootPane.requestFocusInWindow();
} catch (Exception ex) {
System.out.println("Failed loading L&F: " + laf);
System.out.println(ex);
}
}
}
private class ExitAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public ExitAction() {
putValue(Action.NAME, "Exit");
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_X));
}
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
private class DummyFileSystemView extends FileSystemView {
#Override
public File createNewFolder(File containingDir) {
return null;
}
#Override
public File getDefaultDirectory() {
return null;
}
#Override
public File getHomeDirectory() {
return null;
}
}
private static void createAndShowGUI() {
KeyBindings application = new KeyBindings();
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Key Bindings");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(application.getMenuBar());
frame.getContentPane().add(application.getContentPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
//UIManager.put("swing.boldMetal", Boolean.FALSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
If you want to run "global events" on your application regardless of where you your current focus is, you'll need to work with the KeyboardFocusManager :
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
kfm.addKeyEventDispatcher(new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
// do your stuff here
return done;
}
});
Hope this helps.
I'm using SWT (and Eclipse RCP) to render a table. My problem is that if I change the background of a cell (a ViewerCell in fact) I can see that it has the new color.
My problem is that if I select a row in my Table or if I hover over the row containing my cell in question then the selection/hover background overrides my cell color. How can I override this?
Problem solved with StyledCellLabelProvider. Tell me if you want to see some code.
Edit:
We use it do display validation errors so ignore the validation stuff here:
public class ValidationCellLabelProvider extends StyledCellLabelProvider {
private static final int DELAY = 200;
private static final int SHIFT_X = 5;
private static final int SHIFT_Y = 5;
private static final int DISPLAY = 5000;
private CellLabelProvider provider;
private String propertyName;
private final StyleRange[] styleRanges = new StyleRange[1];
/**
* Default constructor.
* #param provider provider
* #param propertyName propertyName
*/
public ValidationCellLabelProvider(CellLabelProvider provider, String propertyName) {
super(StyledCellLabelProvider.COLORS_ON_SELECTION);
this.provider = provider;
this.propertyName = propertyName;
this.setOwnerDrawEnabled(true);
}
#Override
public void initialize(ColumnViewer viewer, ViewerColumn column) {
super.initialize(viewer, column);
final StyleRange styleRange = new StyleRange();
styleRange.foreground = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
styleRange.background = Display.getCurrent().getSystemColor(SWT.COLOR_RED);
styleRanges[0] = styleRange;
}
#Override
public void update(ViewerCell cell) {
provider.update(cell);
if (cell.getStyleRanges() == null) {
cell.setStyleRanges(styleRanges);
}
if (cell.getElement() instanceof IValidable) {
IValidable model = (IValidable) cell.getElement();
if (!ControllerRegistry.getCurrentViolations().getViolations(model.getModelId(), propertyName).isEmpty()) {
if (cell.getText().isEmpty()) {
cell.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
cell.setImage(FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage());
} else {
if (styleRanges[0].length < cell.getText().length()) {
styleRanges[0].length = cell.getText().length();
}
}
} else {
if (cell.getImage() != null) {
cell.setImage(null);
}
cell.setStyleRanges(null);
}
}
super.update(cell);
}
//mine
#Override
protected void paint(Event event, Object element) {
if (element instanceof IValidable) {
IValidable model = (IValidable) element;
if (!ControllerRegistry.getCurrentViolations().getViolations(model.getModelId(), propertyName).isEmpty()) {
int width = 1000;
int x = event.x;
int y = event.y;
int height = event.height - 1;
GC gc = event.gc;
Color oldBackground = gc.getBackground();
gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED));
gc.fillRectangle(x, y, width, height);
gc.setBackground(oldBackground);
}
}
super.paint(event, element);
}
//-----
#Override
public String getToolTipText(Object element) {
String ret = null;
if (element instanceof IValidable) {
List<ConstraintViolation> constraintViolations = ControllerRegistry.getCurrentViolations().getViolations(
((IValidable) element).getModelId(), propertyName);
if (!constraintViolations.isEmpty()) {
ret = ValidationHelper.getMessage(constraintViolations);
}
}
if (ret != null) {
ret = ret.length() > 0 ? ret.toString() : null;
}
return ret;
}
#Override
public int getToolTipDisplayDelayTime(Object object) {
return DELAY;
}
#Override
public Point getToolTipShift(Object object) {
return new Point(SHIFT_X, SHIFT_Y);
}
#Override
public int getToolTipTimeDisplayed(Object object) {
return DISPLAY;
}
}
The only option I see would be use an OwnerDrawLabelProvider and paint the whole cell yourself.
There is a way to prevent the table from drawing its selection background but the font color will still change to its selection color, so depending on your OS you might end up with white text on white background when a row is selected.
I have added multiple JProgressBar to TableColumn of JTable.
I am updating all the JProgressBar with data after making certain calculations, but only the last ProgressBar(in this case ProgressBar progressObj4) which is added is getting updated.
How can I update all the ProgressBars?
The basic requirement is that I am displaying the status of file in progress bar while uploading . Currently I am hardcoding 4 progress bars to test if all the progress bars are getting update wrt the status of the file, but I need to create them dynamically. The total no of progress bars wrt the no of files which is getting uploaded. Also, how can I fetch the individual instances of the progress bars & update their status ?
I am attaching the source code of the progressbar getting added to the table column.
//tc = object of TableColumn
progressObj1 = new ProgressBarRenderer("Progress1");
progressObj1.setValue(0);
progressObj1.setStringPainted(true);
progressObj1.setBackground(Color.WHITE);
progressObj1.setBorderPainted(true);
tc.setCellRenderer(progressObj1);
progressObj2 = new ProgressBarRenderer("Progress2");
progressObj2.setValue(0);
progressObj2.setStringPainted(true);
progressObj2.setBackground(Color.WHITE);
progressObj2.setBorderPainted(true);
tc.setCellRenderer(progressObj2);
progressObj3 = new ProgressBarRenderer("Progress3");
progressObj3.setValue(0);
progressObj3.setStringPainted(true);
progressObj3.setBackground(Color.WHITE);
progressObj3.setBorderPainted(true);
tc.setCellRenderer(progressObj3);
progressObj4 = new ProgressBarRenderer("Progress4");
progressObj4.setValue(0);
progressObj4.setStringPainted(true);
progressObj4.setBackground(Color.WHITE);
progressObj4.setBorderPainted(true);
tc.setCellRenderer(progressObj4);
basically there are two ways move with JProgressBar by using SwingWorker and Runnable#Thread, example for SwingWorker
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableCellProgressBar {
private String[] columnNames = {"String", "ProgressBar"};
private Object[][] data = {{"dummy", 100}};
private DefaultTableModel model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
return false;
}
};
private JTable table = new JTable(model);
public JComponent makeUI() {
TableColumn column = table.getColumnModel().getColumn(1);
column.setCellRenderer(new ProgressRenderer());
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
startTask("test");
startTask("error test");
startTask("test");
}
});
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
//http://java-swing-tips.blogspot.com/2008/03/jprogressbar-in-jtable-cell.html
private void startTask(String str) {
final int key = model.getRowCount();
SwingWorker<Integer, Integer> worker = new SwingWorker<Integer, Integer>() {
private int sleepDummy = new Random().nextInt(100) + 1;
private int lengthOfTask = 120;
#Override
protected Integer doInBackground() {
int current = 0;
while (current < lengthOfTask && !isCancelled()) {
if (!table.isDisplayable()) {
break;
}
if (key == 2 && current > 60) { //Error Test
cancel(true);
publish(-1);
return -1;
}
current++;
try {
Thread.sleep(sleepDummy);
} catch (InterruptedException ie) {
break;
}
publish(100 * current / lengthOfTask);
}
return sleepDummy * lengthOfTask;
}
#Override
protected void process(java.util.List<Integer> c) {
model.setValueAt(c.get(c.size() - 1), key, 1);
}
#Override
protected void done() {
String text;
int i = -1;
if (isCancelled()) {
text = "Cancelled";
} else {
try {
i = get();
text = (i >= 0) ? "Done" : "Disposed";
} catch (Exception ignore) {
ignore.printStackTrace();
text = ignore.getMessage();
}
}
System.out.println(key + ":" + text + "(" + i + "ms)");
}
};
model.addRow(new Object[]{str, 0});
worker.execute();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new TableCellProgressBar().makeUI());
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ProgressRenderer extends DefaultTableCellRenderer {
private final JProgressBar b = new JProgressBar(0, 100);
public ProgressRenderer() {
super();
setOpaque(true);
b.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Integer i = (Integer) value;
String text = "Completed";
if (i < 0) {
text = "Error";
} else if (i < 100) {
b.setValue(i);
return b;
}
super.getTableCellRendererComponent(table, text, isSelected, hasFocus, row, column);
return this;
}
}
To my knowledge, a renderer applies to all rows in a given column. It seems to me that you would like to apply several renderers to the same column. Furthermore, it also seems that you are attempting to give the cell renderer state.
I believe it will help to make the renderer stateless and have the renderer's method getTableCellRendererComponent() take care of setting the various (JProgressBar) properties based on values from the current row before the renderer paints the cell.
In other words, you should only need to invoke tc.setCellRenderer() once for any given column, and then have your cell renderer to draw the column for any given row (based on, say, the underlying data model for that table).
I am trying to find the color used by cells that are unselected. This is usually white, however when I call UIManager.getColor("List.background"), it appears to be the same grey color used for JPanels. When I call new JList().getBackground(), I get the same horrid grey color back, but when I actually use the list, it's white. How do I get this white color from JList or UIManager? What I am currently doing to find the background color is this:
String[] contents = {"Foo", "Bar"};
JList list = new JList(contents);
// Prints true
System.out.println(list.getBackground().equals(new Color(237, 236, 235)));
Since List.selectionBackground gives me the blue color I expect to see when I click on a cell, I figured List.background would give me the color of an unselected cell. What is List.background actually returning a value for then?
On related note, is there a listing somewhere of what these keys means? I've found a related question, but none of the answers provide descriptions of the keys.
EDIT: It appears this is the correct way to do this. However, at least in GNOME the problem arises when calling setLookAndFeel.
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
String[] contents = {"Foo", "Bar"};
JList list = new JList(contents);
// Prints true
System.out.println(list.getBackground().equals(new Color(237, 236, 235)));
// Add list to a pane and display it, and it will actually be white
Looks like this might be a bug, sorry guys.
SwingUtilities.updateComponentTreeUI(myFrame);
EDIT:
hmmm, can you clarify me (us) what is your issue based on this example (from died Old.Good.Forum.Sun.com, :-) and I know that not Jeanette resist)
Note: better would be change this value by using getListCellRendererComponent
EDIT2: please for another play with that check camickr UI Defaluts
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JListDisabledItemDemo implements ItemListener, Runnable {
private static final String ITEMS[] = {"Black", "Blue", "Green", "Orange", "Purple", "Red", "White", "Yellow"};
private JList jList;
private JCheckBox[] checkBoxes;
private boolean[] enabledFlags;
#Override
public void run() {
JPanel pnlEnablers = new JPanel(new GridLayout(0, 1));
pnlEnablers.setBorder(BorderFactory.createTitledBorder("Enabled Items"));
checkBoxes = new JCheckBox[ITEMS.length];
enabledFlags = new boolean[ITEMS.length];
for (int i = 0; i < ITEMS.length; i++) {
checkBoxes[i] = new JCheckBox(ITEMS[i]);
checkBoxes[i].setSelected(true);
checkBoxes[i].addItemListener(this);
enabledFlags[i] = true;
pnlEnablers.add(checkBoxes[i]);
}
jList = new JList(ITEMS);
jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jList.setSelectionModel(new DisabledItemSelectionModel());
jList.setCellRenderer(new DisabledItemListCellRenderer());
jList.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
System.out.println("selection");
}
}
});
JScrollPane scroll = new JScrollPane(jList);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
JFrame f = new JFrame("Colors");
Container contentPane = f.getContentPane();
contentPane.setLayout(new GridLayout(1, 2));
contentPane.add(pnlEnablers);
contentPane.add(scroll);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(240, 280);
f.setLocationRelativeTo(null);
UIManager.put("List.background", Color.red);
UIManager.put("List.selectionBackground", Color.orange);
UIManager.put("List.selectionForeground", Color.blue);
UIManager.put("Label.disabledForeground", Color.magenta);
SwingUtilities.updateComponentTreeUI(f);
f.setVisible(true);
}
#Override
public void itemStateChanged(ItemEvent event) {
JCheckBox checkBox = (JCheckBox) event.getSource();
int index = -1;
for (int i = 0; i < ITEMS.length; i++) {
if (ITEMS[i].equals(checkBox.getText())) {
index = i;
break;
}
}
if (index != -1) {
enabledFlags[index] = checkBox.isSelected();
jList.repaint();
}
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new JListDisabledItemDemo());
}
private class DisabledItemListCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 1L;
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (enabledFlags[index]) {
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
Component comp = super.getListCellRendererComponent(list, value, index, false, false);
comp.setEnabled(false);
return comp;
}
}
private class DisabledItemSelectionModel extends DefaultListSelectionModel {
private static final long serialVersionUID = 1L;
/**
* No need to override addSelectionInterval(int index0, int index1)
* since we're using SINGLE_SELECTION mode for this demo.
*/
#Override
public void setSelectionInterval(int index0, int index1) {
if (enabledFlags[index0]) {
super.setSelectionInterval(index0, index0);
} else {
/*
* The previously selected index is before this one,
* so walk forward to find the next selectable item.
*/
if (getAnchorSelectionIndex() < index0) {
for (int i = index0; i < enabledFlags.length; i++) {
if (enabledFlags[i]) {
super.setSelectionInterval(i, i);
return;
}
}
} /*
* Otherwise, walk backward to find the next selectable item.
*/ else {
for (int i = index0; i >= 0; i--) {
if (enabledFlags[i]) {
super.setSelectionInterval(i, i);
return;
}
}
}
}
}
}
}
I have a java applet in which I have to display a large amount of items (dictionary entries). The user needs to be able to select individual items in the list, hence it is implemented as a JList. Generating the list was very quick until I decided to make the display more asthetically pleasing by formatting the individual items using HTML. Now the list looks pretty but it takes between 10 and 15 seconds to generate it each time the user accesses the dictionary (without formatting it occurs almost instantly). I suppose I could take the performance hit up front by generating the list when the user first enters the application and just hiding and unhiding the list as needed. But, I'm wondering if there is a better way. Perhaps a more efficient way to generate the list.
Here is the section of code where the slow down occurrs (Between the display of C and D):
DefaultListModel dictCodeModel = new DefaultListModel();
System.out.println("C");
while (e.hasMoreElements()) {
String currentEntry = "";
DEntry dentry = (DEntry) e.nextElement();
if (!filter)
dictCodeModel.addElement(dentry.theToken); // tokens have embedded html tags
}
System.out.println("D");
As you can see it is pretty simple. When "theToken" is formatted as HTML, I get a real performance hit. Any ideas of what I can do to get around this?
Thanks,
What kind of HTML formatting are you using? If it's just some text styling (font, color), you can use a JLabel, set its properties accordingly and set it as ListCellRenderer for the JList.
The links above are a bit out of date so here's something more up to date.
Simply using JTable is a huge improvement in speed on initial load but a little slow when you first start scrolling. And you have the new problem that the row height needs adjusting. This can be done inside of a custom renderer easy enough by implementing TableCellRenderer since the getTableCellRendererComponent method gives you access to the row, the table and the component. This will however fire a update of the table which will call the same code. If you code appropriately, this won't be a problem. Still, it's better practice to put it somewhere else. I added a listener to the JViewport and only updated the rows that are currently in view. The code I based this on is here
Alternatively, you can use write a ListCellRenderer that returns a JPanel that looks like the HTML. If you only need a JTextArea then you'll need to set its width to ensure it's preferred height is set correctly like in this answer. Again, you have to update the row's width and it makes sense to do this based on the JViewport.
If you're curious about the performance of both approaches, then a custom renderer returning a JPanel is faster than JLabels rendering HTML. Both are reasonably quick though even with lists with a few thousand items. As mentioned, they can be a little slow when you initially scroll.
Finally, here's some code that lets you make a quick comparison yourself:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.Timer;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class JTableHtmlTest extends JFrame {
protected static final long serialVersionUID = 1L;
public static class Item {
public int id;
public String msg;
}
static class TableModel extends AbstractTableModel {
private static final long serialVersionUID = JListTest.serialVersionUID;
private Item[] items = new Item[] {};
public int getRowCount() {
return items.length;
}
public int getColumnCount() {
return 1;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return items[rowIndex];
}
#Override
public String getColumnName(int column) {
return "";
}
public void updateItems() {
SwingWorker<Item[], Void> worker = new SwingWorker<Item[], Void>() {
#Override
protected Item[] doInBackground() throws Exception {
final Item[] tempList = new Item[3000];
for (int i = 0; i < tempList.length; i++) {
Item item = new Item();
item.id = (int) (Math.random() * 10000);
item.msg = "This is the default message that has to be"
+ " long enough to wrap around a few times so that"
+ " we know things are working. It's rather tedious to write.";
tempList[i] = item;
}
return tempList;
}
#Override
protected void done() {
try {
items = get();
fireTableDataChanged();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
worker.execute();
}
}
public static class TableRenderer implements TableCellRenderer {
private static final String strColor = "#EDF5F4";
private static final Color strideColor = Color.decode(strColor);
JLabel htmlLabel = new JLabel();
JPanel noHtmlPanel = new JPanel();
JLabel noHtmlLabel = new JLabel();
JTextArea noHTMLTextArea = new JTextArea();
Item toRender = null;
boolean useHtml = false;
public TableRenderer() {
noHTMLTextArea.setWrapStyleWord(false);
noHTMLTextArea.setLineWrap(true);
noHTMLTextArea.setOpaque(false);
Font defaultFont = noHtmlLabel.getFont();
Font boldFont = defaultFont.deriveFont(Font.BOLD);
noHtmlLabel.setFont(boldFont);
noHtmlLabel.setOpaque(false);
noHtmlPanel.setLayout(new BorderLayout());
noHtmlPanel.add(noHtmlLabel, BorderLayout.NORTH);
noHtmlPanel.add(noHTMLTextArea, BorderLayout.SOUTH);
}
public void setUseHtml(boolean useHtml) {
this.useHtml = useHtml;
}
public Component getJlabelRenderer(JTable table, Item value, int row) {
String colorString = "";
if (row % 2 == 0) {
colorString = "background-color:" + strColor + ";";
}
if (toRender != value) {
toRender = value;
htmlLabel.setText("<html><div style='padding:2px;" + "width:"
+ table.getWidth() + ";" + colorString
+ "color:black;'>"
+ "<div style='padding:2px;font-weight:500;'>"
+ "Item " + value.id + "</div>" + value.msg
+ "</div></html>");
}
return htmlLabel;
}
public Component getNoHtmlRenderer(JTable table, Item value, int row) {
if (toRender != value) {
toRender = value;
noHtmlLabel.setText("Item " + value.id);
noHTMLTextArea.setText(value.msg);
if (row % 2 == 0) {
noHtmlPanel.setBackground(strideColor);
noHtmlPanel.setOpaque(true);
} else {
noHtmlPanel.setOpaque(false);
}
}
return noHtmlPanel;
}
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
if (useHtml) {
return getJlabelRenderer(table, (Item) value, row);
} else {
return getNoHtmlRenderer(table, (Item) value, row);
}
}
}
public JTableHtmlTest() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel controlPanel = new JPanel();
JButton updaterControl = new JButton("Update 3000");
final JCheckBox useHtmlControl = new JCheckBox("Use HTML");
final TableModel model = new TableModel();
final JTable table = new JTable(model);
final TableRenderer renderer = new TableRenderer();
JScrollPane scrollPane = new JScrollPane(table);
final JLabel durationIndicator = new JLabel("0");
controlPanel.add(useHtmlControl, BorderLayout.WEST);
controlPanel.add(updaterControl, BorderLayout.EAST);
getContentPane().add(controlPanel, BorderLayout.PAGE_START);
getContentPane().add(scrollPane, BorderLayout.CENTER);
getContentPane().add(durationIndicator, BorderLayout.PAGE_END);
table.setDefaultRenderer(Object.class, renderer);
// Only update the JTable row heights when they are in view
final JViewport viewport = scrollPane.getViewport();
viewport.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
Rectangle viewRect = viewport.getViewRect();
int first = table.rowAtPoint(new Point(0, viewRect.y));
if (first == -1) {
return;
}
int last = table.rowAtPoint(new Point(0, viewRect.y
+ viewRect.height - 1));
if (last == -1) {
last = model.getRowCount() - 1;
}
int column = 0;
for (int row = first; row <= last; row++) {
Component comp = table.prepareRenderer(
table.getCellRenderer(row, column),
row, column);
int rowHeight = comp.getPreferredSize().height;
table.setRowHeight(row, rowHeight);
}
}
});
updaterControl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
renderer.setUseHtml(useHtmlControl.isSelected());
model.updateItems();
}
});
Timer counter = new Timer();
counter.schedule(new TimerTask() {
#Override
public void run() {
String previousCounter = durationIndicator.getText();
final String newCounter = Integer.toString(Integer
.parseInt(previousCounter) + 1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
durationIndicator.setText(newCounter);
setTitle(newCounter);
}
});
}
}, 0, 100);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JTableHtmlTest jlt = new JTableHtmlTest();
jlt.pack();
jlt.setSize(300, 300);
jlt.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}