Made a custom ListCellRenderer:
import java.awt.Component;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
/**
*
* #author Spencer
*/
public class TaskRenderer implements ListCellRenderer {
private Task task;
private JPanel panel = new JPanel();
private JCheckBox checkbox = new JCheckBox();
private JLabel label = new JLabel();
public TaskRenderer() {
panel.add(checkbox);
panel.add(label);
}
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
task = (Task) value;
label.setText(task.getName());
return panel;
}
}
Have a JList with each cell in it rendered using the above class, but the checkboxes in the panels for each cell cannot be clicked. Thought it had to do with it not getting focus. Any ideas?
Thanks,
Spencer
Your custom renderer is simply governing the appearance of the JList contents, not adding any functionality such as the ability to modify the components (check box) - Imagine it simply as a rubber stamp used to display each list cell in turn.
I'd recommend solving the problem by:
Use a single-column JTable instead of a JList.
Define a bespoke TableModel implementation by sub-classing AbstractTableModel and override getColumnClass(int) to return Boolean.class for column 0. Note that the default renderer will now render this as a JCheckBox. However, it will not be a labelled JCheckBox as you require.
Add a bespoke TableCellRenderer for Booleans; e.g. myTable.setCellRenderer(Boolean.class, new MyLabelledCheckBoxRenderer());
Add an editor for Booleans, using something similar to: myTable.setCellEditor(Boolean.class, new DefaultEditor(new JCheckBox("Is Enabled)));
JIDE Common Layer has a GPL'ed CheckBoxList. Basically it uses a JPanel as cell renderer with a JCheckBox in front of another renderer (which you can set yourself), and handles mouse/key events.
If you really want to stick to your JCheckBox renderer, you can listen to mouse/key events and process them appropriately. Keep in mind that, as Adamski pointed out, cell renderer is a rubber stamp (Swing 101) so you have to always set the check box selected state in getListCellRendererComponent(), otherwise all your checkboxes will have the save state.
Related
I spent the last few hours looking for a solution or at least a decent guide about this issue, but found nothing.
I'm implementing a custom Swing Look and Feel for a small GUI of mine. Up until now I've been using the UIManager.put("key", values); method to good success, but I got stuck when it came to properly modify JComboBoxes.
Using this list I managed to get my jComboBoxes really close to what I want them to look like:
I have two issues with this, a major and a minor one:
Major
I want the blue shown border gone.
Minor
I'd really like the black shown border gone.
Apparently no key in the UIDefaults has anything to do with either two borders: they seem somehow hardcoded in the Look and Feel I'm modifying (which should be Metal). I resorted to manually extending the ComboBoxRenderer and managed to come up with this:
package exec.laf.theme;
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
public class ComboBoxRenderer extends BasicComboBoxRenderer {
private Color background;
private Color selectionBackground;
public ComboBoxRenderer() {
super();
background = UIManager.getColor("ComboBox.background");
selectionBackground = UIManager.getColor("ComboBox.selectionBackground");
}
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
setText((String) value);
if (isSelected) setBackground(selectionBackground);
else setBackground(background);
return this;
}
}
Where I specify this Renderer every time I create a JComboBox like so:
aComboBox.setRenderer(new ComboBoxRenderer());
obtaining the same look as the non-extended JComboBox.
The problem is that with this extension I can't find a way of touching those borders. Adding setBorder(new EmptyBorder(0, 0, 0, 0)); accomplishes nothing, since it simply adds a border to the listed items.
I checked the source code for javax.swing.plaf.basic.BasicComboBoxRenderer to see if any borders were applied there, but found nothing (the only border there is the one applied to the listed items, that I can override as shown above.
What am I supposed to do? Am I extending the wrong class, or am I missing something else?
The solution I found is:
UIManager.put("ComboBox.borderPaintsFocus", Boolean.TRUE)
This sets a boolean inside the ComboBoxUI that prevents rendering of the focus border, which is the border painted around all buttons when they are focused. Its style is dependent on your look and feel.
To remove the black border of comboBox PopUp,
Object child = comboBox.getAccessibleContext().getAccessibleChild(0);
BasicComboPopup popup = (BasicComboPopup)child;
popup.setBorder(BorderFactory.createEmptyBorder());
If i understoud, your problem is in generally how to use extended class from BasicComboBoxRenderer. So here a simple code to explain you how to use it:
public class RenderComboBox extends BasicComboBoxRenderer {
Color selectedBackground;
Color selectedForground;
Color background;
Color forground;
public RenderComboBox() {
setOpaque(true);
background = new Color(37, 37, 37);
selectedBackground = new Color(93, 93, 93);
forground = Color.WHITE;
selectedForground = forground;
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (isSelected) {
setBackground(selectedBackground);
setForeground(selectedForground);
} else {
setBackground(background);
setForeground(forground);
}
setFont(list.getFont());
if (value == null) {
setText("");
} else {
setText(value.toString());
}
return this;
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setLayout(null);
frame.setPreferredSize(new Dimension(200, 170));
JComboBox<String> combobox = new JComboBox<>();
combobox.setRenderer(new RenderComboBox());
combobox.setBounds(50, 50, 100, 20);
combobox.addItem("TEST");
combobox.addItem("REVERT");
frame.add(combobox);
frame.pack();
frame.setVisible(true);
}
}
When a JComboBox is just made and added and all the background of the selected item is just normal and white:
(ignore the enormous spacing after the text)
When I then open the list and hover my cursor over an item, that item get's highlighted, all normal, nothing wrong with it.
But the problem now is that the highlighting stays once I've clicked on an item:
So my question is:
How can I make the highlighting go away?
Preferably without doing difficult with packages from the community or overloading or whatever.
If I'm right it has to be in the 'root' of the action listener of the combo box?
So:
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == comboBox)
{
// code to delete the highlighting
}
}
The highlighting is done by the default renderer for the combo box.
See the section from the Swing tutorial on Providing Custom Renderers for an example of providing your own custom renderer. You will just want a renderer that doesn't change the background/foreground depending on the selected value.
To make it easier for people with a similar problem, here is the code for the renderer I wrote:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ComboBoxRenderer extends JLabel implements ListCellRenderer
{
private boolean colorSet;
private Color selectionBackgroundColor;
public ComboBoxRenderer()
{
setOpaque(true);
setHorizontalAlignment(LEFT);
setVerticalAlignment(CENTER);
colorSet = false;
selectionBackgroundColor = Color.red; // Have to set a color, else a compiler error will occur
}
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
// Check if color is set (only runs the first time)
if(!colorSet)
{
// Set the list' background color to original selection background of the list
selectionBackgroundColor = list.getSelectionBackground();
// Do this only one time since the color will change later
colorSet = true;
}
// Set the list' background color to white (white will show once selection is made)
list.setSelectionBackground(Color.white);
// Check which item is selected
if(isSelected)
{
// Set background color of the item your cursor is hovering over to the original background color
setBackground(selectionBackgroundColor);
}
else
{
// Set background color of all other items to white
setBackground(Color.white);
}
// Do nothing about the text and font to be displayed
setText((String)value);
setFont(list.getFont());
return this;
}
}
Edit: Previous code didn't seem to work as properly, code updated and should work all fine now
I have a JTable that just contains one item which is a JButtton that has an icon which is set to the JFreeChart generated chart.
If you click the button it launches a new window with the JFreeChart chart.
I have a cell renderer for the button which simply returns the JButton object that contains the chart item.
If I single click on the button it will launch the new window.
But if I double click on the button the new window launches, but the icon on the button, which used to be the JFreeChart, changes to just a text string with the class name path of the JButton with the class field values.
They other way I can get this behavior to happen is if I remove the cell renderer then the JButton just diplays the class name. So I'm not sure what is going on and have played around with it a ton and haven't made any progress. I was going to post some code but there is just too much of it.
If anybody has any ideas, I can post certain sections of code to help explain.
Here is code:
Class init {
...
ItModel itModel = new ItModel ();
JTable table = new JTable(itModel );
TableRendrend rend = new TableRend();
table.getColumn("it").setCellRenderer(rend);
}
class TableRend implements TableCellRenderer {
JButton itButton;
...
getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int columns){
if(itButton ==null){
itButton = new JButton (JButton)value.getIcon();
}
return itButton;
}
}
Class itModel extends AbstractTableModel{
JButton button;
...
public Object getValueAt(int rowIndex, intColumnIndex){
if(button==null){
button = new JButton( new ImageIcon(JFreeChartImage.getImage()));
}
return button
}
}
This all works except when double clicking on the JButton and it displays TEXT ex.
javax.Swing.JButton(0,0,...)
all field values instead of the the actual chart image that should be displayed
There are several issues with the code you posted, and too long to include them all in a comment.
You should not store Swing components in the model. The creation of the components is the task for the renderer
I do not believe you can click the button which is returned by the renderer. That button is not contained in the JTable which is displayed. Only an image/screenshot/stamp of the button is drawn on-screen, but it does not behave like a button. You would need an editor for that. Consult the JTable tutorial about the editors and renderers for more information
I am trying to display HTML text inside a JTable's cell but the scrollbars arent showing up at all. Below is my code...
public class TableCellTextRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
final JTextPane jtextPane = new JTextPane();
JScrollPane scrollPane = new JScrollPane(jtextPane);
scrollPane.setPreferredSize(new Dimension(350,350));
scrollPane.setVisible(true);
jtextPane.setEditable(false);
jtextPane.setAutoscrolls(true);
jtextPane.setContentType("text/html");
jtextPane.setText(myHtmlText);
jtextPane.setVisible(true);
jtextPane.setPreferredSize(new Dimension(350,350));
//this setViewPort has no effect
scrollPane.setViewportView(jtextPane);
jtextPane.setVisible(true);
scrollPane.setAutoscrolls(true);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
JPanel jpanel = new JPanel();
jpanel.setVisible(true);
jpanel.setPreferredSize(new Dimension(360, 360));
jpanel.add(scrollPane);
return jpanel;
}
}
The scroll bars appear, but the scrolling handles dont appear. Can you please tell me what I am missing?
The table looks like this:
I'm afraid this is not going to work the way you'd want it to. The way JTables work is by obtaining a renderer component to draw the cell once, then cache it. In simplest terms, the component you're returning is basically just used to draw the cell, but it does not work the way normal components do - for instance, it does not receive events. You can use a cell editor (which will receive events), but the user would have to select the cell manually.
There are a couple of tricks you can use by installing custom mouse listeners, but this stuff will get very complicated. It'd probably be better to ask yourself if you're really looking for a JTable (as opposed to a custom layout), or if it would perhaps be possible to embed a JavaFX table.
I have a cell editor that contains a little button that can be double clicked on to bring up an edit dialog, and then a textfield that can be used to edit the value inline (the popup is required to allow editing of additional values, only the first is shown in the JTable).
When user clicks on field everything is okay, but if they tab into the field they textfield doesn't receive focus and they cannot edit the field unless they click on it with the mouse.
I tried fiddling with the various focus methods of jpanel but it made no difference, anybody know what Im doing wrong ?
package com.jthink.jaikoz.celleditor;
import com.jthink.jaikoz.celldata.Cell;
import com.jthink.jaikoz.guielement.Focus;
import com.jthink.jaikoz.table.CellLocation;
import com.jthink.jaikoz.table.DatasheetToggleButton;
import com.jthink.jaikoz.table.datasheet.Datasheet;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
public class SimpleMultiRowCellEditor
extends DefaultCellEditor implements ActionListener
{
final JPanel panel;
private final DatasheetToggleButton rowCount;
Cell value;
public SimpleMultiRowCellEditor(final JTextField text)
{
super(text);
this.setClickCountToStart(1);
rowCount = new DatasheetToggleButton();
rowCount.setVisible(true);
rowCount.addActionListener(this);
panel = new JPanel();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.add(rowCount);
panel.add(editorComponent);
/*panel.setFocusable(true);
panel.setFocusCycleRoot(true);
ArrayList focusOrder = new ArrayList();
focusOrder.add(editorComponent);
focusOrder.add(rowCount);
focusOrder.add(panel);
panel.setFocusTraversalPolicy(new Focus(focusOrder));
*/
}
public Component getTableCellEditorComponent(
final JTable table, final Object val, final boolean isSelected,
final int row, final int column)
{
value = (Cell) ((Cell) val).clone();
rowCount.setText(String.valueOf(value.getValues().size()));
delegate.setValue(value.getValue());
return panel;
}
public Object getCellEditorValue()
{
final String s = (String) delegate.getCellEditorValue();
value.setValue(s);
return value;
}
public void actionPerformed(final ActionEvent e)
{
this.stopCellEditing();
final CellLocation cl = Datasheet.getActiveEditSheet()
.getTable().getSelectedCellLocations().get(0);
UpdateMultiRowCellDialog.getInstanceOf().display(value,cl);
}
}
Tried adding focuslistener to panel, didnt seem to make any difference
class PanelFocusListener implements FocusListener
{
public void focusGained(FocusEvent e)
{
System.out.println("Gained Focus");
editorComponent.requestFocusInWindow();
}
public void focusLost(FocusEvent e)
{
System.out.println("Lost Focus");
}
}
So after tabbing into field, I type a key and it sorts of look likes focus is gained but you cannot enter anything into the field whereas if I type RETURN then I can start editing the field, what does pressing RETURN do that allows it to work ?
what does pressing RETURN do that allows it to work?
As shown in the handy Key Bindings application, the default ENTER key binding in most L&Fs is notify-field-accept. It's not clear why your ActionListener begins with stopCellEditing(). I would have expected it to invoke fireEditingStopped() after updating the data model, as suggested in this example.
Sadly, I'm unfamiliar with Jaikoz. You might look at Concepts: Editors and Renderers and the subsequent sections for additional guidance.
Addendum: As noted in your comment, a JTextField in a DefaultCellEditor allows typing in the selected field by default. It's not clear from your example how that default is being nullified. Absent an sscce that demonstrates the problem, you might compare your code with this related example that exhibits the default behavior using a subclass of JTextField.