This is an example of a JList. I know how to set a border to the entire JList or the panel containing the JList. My question is that how can we create line borders between the cells?`
For example in the given picture the color of the line border between the first cell and the second is grey, while there is a white colored line border between all the other cells.
Please don't say that this is a JComboBox or a JTree (because of the second element and its two children); even if it is not a JList, I want my JList to have similar LineBorders between the cells.
My web search led me to this interface. Its method getListCellRendererComponent takes arguments like E value, int index, boolean isSelected, boolean cellHasFocus, while I want the LineBorder to appear between all the cells no matter what index they have and whether they are selected or not etc.
EDIT:-
The border is thicker in some places and fine in others.
You can achive that with help of ListCellRenderer. Here is simple example:
import java.awt.Color;
import java.awt.Component;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
public class TestFrame extends JFrame{
public TestFrame(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
pack();
setVisible(true);
}
private void init() {
JList<String> list = new JList<>(new String[]{"1","2","3"});
list.setCellRenderer(getRenderer());
add(list);
}
private ListCellRenderer<? super String> getRenderer() {
return new DefaultListCellRenderer(){
#Override
public Component getListCellRendererComponent(JList<?> list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
JLabel listCellRendererComponent = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected,cellHasFocus);
listCellRendererComponent.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0,Color.BLACK));
return listCellRendererComponent;
}
};
}
public static void main(String... strings) {
new TestFrame();
}
}
Looks like:
Read more in tutorial.
EDIT: Just change LineBorder to MatteBorder(have changed code and image).
Related
I customized a Jtable headers with a TableCellRenderer so filter icon would appear, and as the user clicks on the filter icon, a pop-up filter like excel's appears (picture 1). However, if the user clicks on the text of the header, a row sorter is appliead. When the row sorter is applied (picture 2), it override the customization and filter icon disappears. Is there a way to avoid this behavior while keeping the row sorter on the table header?
You can do this by decorating the original renderer of the table header with a custom one which will add the desired icon of filtering.
For example, follows some code to preserve the sort icons while adding your own label next to the original column header:
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.util.Objects;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
public class Main {
private static class MyTableCellRenderer extends DefaultTableCellRenderer {
private final TableCellRenderer originalRenderer;
public MyTableCellRenderer(final TableCellRenderer originalRenderer) {
this.originalRenderer = Objects.requireNonNull(originalRenderer);
}
#Override
public Component getTableCellRendererComponent(final JTable table,
final Object value,
final boolean isSelected,
final boolean hasFocus,
final int row,
final int column) {
final Component original = originalRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (row >= 0) //The header will have a row equal to -1.
return original;
final JPanel container = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
final JLabel filtering = new JLabel("Filter", JLabel.CENTER); //Your icon should go in this label...
//Put some fancy background to make the positioning of the label clear:
filtering.setOpaque(true);
filtering.setBackground(Color.CYAN);
//The default renderer comes with a border... Let's apply the border to the whole new container:
if (original instanceof JComponent) {
container.setBorder(((JComponent) original).getBorder());
((JComponent) original).setBorder(null);
}
container.add(filtering);
container.add(original);
return container;
}
}
private static void createAndShowGUI() {
final JTable table = new JTable(new Object[][] {
new Object[]{"Data001", "Data002", "Data003"},
new Object[]{"Data011", "Data012", "Data013"},
new Object[]{"Data021", "Data022", "Data023"},
new Object[]{"Data031", "Data032", "Data033"},
new Object[]{"Data041", "Data042", "Data043"}
}, new Object[] {"Column1", "Column2", "Column3"});
table.setAutoCreateRowSorter(true);
final JTableHeader header = table.getTableHeader();
header.setDefaultRenderer(new MyTableCellRenderer(header.getDefaultRenderer()));
final JFrame frame = new JFrame("Table header");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}
Instead of the word Filter (which appears on each column header) you can use your icons on the created label instead.
References:
Source (see the section: 3. Keeping sort icons).
How can I put a control in the JTableHeader of a JTable?
I'm using a ListCellRenderer to edit the appearance of entries in a JList. Once they are selected (by clicking them, this code is within a click event) I call the ListCellRenderer to change the color of the text. If they are selected again I want the text to return to the normal color. The problem I am having is that once I select a second entry the first entry goes back to its normal color. How can I keep selected entries the selected color until they are actually deselected? Here is the section where I actually call the function:
for(int i = 0; i < selectedEntries.size() - 1; i++){
System.out.println("Inside the for loop at entry:" + i);
if(selectedEntries.get(i).equals(selectedEntry)){
selectedEntries.remove(i);
removed = true;
renderer.getListCellRendererComponent(logEntries, value, index, false, false);
System.out.println("Duplicate Entry Removed From List");
}
}
if(!removed){
selectedEntries.add(selectedEntry);
renderer.getListCellRendererComponent(logEntries, value, index, true, false);
}
Just for ease of interpretation selectedEntries is an ArrayList containing each selected entry's text.
Once they are selected (by clicking them, this code is within a click event) I call the ListCellRenderer to change the color of the text
No, that's not how it should work, the ListCellRenderer will be called again (by the JList) and the isSelected parameter will be true, to which you would render the values differently
The ListCellRenderer is responsible for rendering the entire state, selected or not selected.
Have a look at Writing a Custom Cell Renderer for more details
For example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
DefaultListModel<String> model = new DefaultListModel<>();
model.addElement("Apples");
model.addElement("Bananas");
model.addElement("Peachs");
model.addElement("Pears");
JList<String> listOfStrings = new JList<>(model);
listOfStrings.setCellRenderer(new FancyPancyListCellRenderer());
add(new JScrollPane(listOfStrings));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class FancyPancyListCellRenderer extends DefaultListCellRenderer {
protected static final Font SELECTED_FONT = new Font("Comic Sans MS", Font.PLAIN, 12);
#Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (isSelected) {
setForeground(Color.YELLOW);
setFont(SELECTED_FONT);
} else {
setFont(UIManager.getFont("Label.font"));
}
return this;
}
}
}
Also, MouseListener really isn't a suitable means by which to detect changes in the selection, what happens if the user selects rows using the keyboard? You should be using a ListSelectionListener instead (but not to correct this issue).
Have a look at How to Write a List Selection Listener and How to Use Lists for more details
For my project i just want to show a dialogue box with JTable. On that i want to show a JCombobox with available staff based on row index. I tried the following coding,
for(int i=0;i<n;i++)
{
Object obj[] = new Object[4];
obj[0]=2,
obj[1]=3;
obj[2]=""; //Here combo appear.
obj[3]=3;
JComboBox aa = new JComboBox();
for(int j=0;j<m;j++)
{
aa.addItem(rs.getString(1));
aa.addItem(rs.getString(2));
}
table.getcolumnModel.getcolumn(2).setcellEditor(new DefaultCellEditor(aa));
model.addRow(obj);
}
if i use this output generated. But last rows combo value is present in all previous rows combo. Those different values are not in that. its totally same. But all other text fields are correctly displayed. What should i do here. thanking you...
Note: Here
aa.addItem(rs.getString(1));
aa.addItem(rs.getString(2));
is just for example. Actually it will return many number of values based on id.
You try to set editor to each row, but it's wrong, editor can be set to whole column. Read Concepts: Editors and Renderers. Instead of that implement your logic in getTableCellEditorComponent() method of TableCellEditor.
Simple example with different values for each row:
import java.awt.Component;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
public class TestFrame extends JFrame {
private DefaultComboBoxModel<String> model;
private Map<String, List<String>> keyVal;
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
keyVal = new HashMap<>();
keyVal.put("1", Arrays.asList(new String[]{"a","b"}));
keyVal.put("0", Arrays.asList(new String[]{"c","d"}));
keyVal.put("2", Arrays.asList(new String[]{"e","f","g"}));
JTable t = new JTable(3,3);
t.getColumnModel().getColumn(0).setCellEditor(getEditor());
add(new JScrollPane(t));
}
private TableCellEditor getEditor() {
return new DefaultCellEditor(new JComboBox<String>(model = new DefaultComboBoxModel<String>())){
#Override
public Component getTableCellEditorComponent(JTable table,Object value, boolean isSelected, int row, int column) {
model.removeAllElements();
if(keyVal.containsKey(row+"")){
List<String> list = keyVal.get(row+"");
for(String s : list)
model.addElement(s);
}
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
};
}
public static void main(String args[]) {
new TestFrame();
}
}
I want to have a JCombobox in my Swing application, which shows the title when nothing is selected. Something like this:
COUNTRY ▼
Spain
Germany
Ireland
I want "COUNTRY" to show when the selected index is -1 and thus, the user wouldn't be able to select it. I tried to put it on the first slot and then overriding the ListCellRenderer so the first element appears greyed out, and handling the events so when trying to select the "title", it selects the first actual element, but I think this is a dirty approach.
Could you lend me a hand?
Overriding the ListCellRenderer is a good approach, but you tried something overly complicated. Just display a certain string if you are rendering the cell -1 and there is no selection (value is null). You are not limited to display elements on the list.
The following is an example program that demonstrates it:
Full Code:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
public class ComboBoxTitleTest
{
public static final void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
new ComboBoxTitleTest().createAndShowGUI();
}
});
}
public void createAndShowGUI()
{
JFrame frame = new JFrame();
JPanel mainPanel = new JPanel();
JPanel buttonsPanel = new JPanel();
frame.add(mainPanel);
frame.add(buttonsPanel, BorderLayout.SOUTH);
String[] options = { "Spain", "Germany", "Ireland", "The kingdom of far far away" };
final JComboBox comboBox = new JComboBox(options);
comboBox.setRenderer(new MyComboBoxRenderer("COUNTRY"));
comboBox.setSelectedIndex(-1); //By default it selects first item, we don't want any selection
mainPanel.add(comboBox);
JButton clearSelectionButton = new JButton("Clear selection");
clearSelectionButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
comboBox.setSelectedIndex(-1);
}
});
buttonsPanel.add(clearSelectionButton);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class MyComboBoxRenderer extends JLabel implements ListCellRenderer
{
private String _title;
public MyComboBoxRenderer(String title)
{
_title = title;
}
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean hasFocus)
{
if (index == -1 && value == null) setText(_title);
else setText(value.toString());
return this;
}
}
}
index == -1 in the renderer is the head component that, by default, displays the selected item and where we want to put our title when there's no selection.
The renderer knows that there's nothing selected because the value passed to it is null, which is usually the case. However if for some weird reasons you had selectable null values in your list, you can just let the renderer consult which is the explicit current selected index by passing it a reference to the comboBox, but that is totally unrealistic.
I have been trying to find a solution for this for the past few days and its driving me crazy. I have a table in which I set the selection colour to yellow. I also set the background of the cell editor component to yellow so that the colour remains the same when the cell is being edited. I do that by overriding the prepareEditor method as such:
#Override
public Component prepareEditor(TableCellEditor editor, int row, int col) {
Component c = super.prepareEditor(editor, row, col);
c.setBackground(Color.YELLOW);
c.setFont(myFont);
return c;
}
This is working fine for all columns except for a column in which I assign a combo box as the cell editor. Once I start editing a cell in that column the background becomes white. The background colour in the popup menu is Yellow but the background colour in the selected value box remains white. I tried adding a focus listener to the combo box but all that I was able to do was change the background of the popup items and not the background of the selected item. I tried adding the focus listener to the combo box itself as such:
myComboBox.addFocusListener(new FocusListener() {//code here});
and to the editor component as such:
myComboBox.getEditor().getEditorComponent().addFocusListener(new FocusListener() {//code here});
and none of these worked. Can someone please point out what Im doing wrong? Thanks.
You probably need to override the cell renderer. Use this in your UI manager and change the paintComponent method to your liking.
public class MyComboBoxUI extends MetalComboBoxUI {
public MyComboBoxUI() {
}
public static ComponentUI createUI(JComponent c) {
return new MyComboBoxUI();
}
#Override
public void installUI(JComponent c) {
ListCellRenderer rend = new ListCellRenderer() {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
final JLabel renderer = new JLabel(value.toString()) {
protected void paintComponent(java.awt.Graphics g) {
UIDefaults uid = UIManager.getDefaults();
Graphics2D g2d = (Graphics2D)g;
Dimension d = this.getSize();
g2d.setPaint(new GradientPaint(0, 0, Color.red, 0, d.height, Color.orange, true));
g2d.fillRect(0, 0, d.width, d.height);
super.paintComponent(g);
}
};
renderer.setOpaque(false);
return renderer;
}
};
super.installUI(c);
((JComboBox)c).setRenderer(rend);
}
}
I managed to find a solution to the problem and Im posting it here in case anyone faces the same problem. the following is the code:
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
public class ComboTest {
JTable table;
JComboBox comboBox;
ComboTest(){
String[] headings = {"Type", "Reference Number", "Amount"};
Object[][] data = {
{"Cash", "123", "2000"},
{"Online", "333", "1200"},
{"Bank Transfer", "667", "800"}
};
comboBox = new JComboBox(new String[] {"Cash", "Cheque", "Bank Transfer", "Credit Card", "Online"});
AutoCompleteDecorator.decorate(comboBox);
JFrame jfrm = new JFrame("Example");
jfrm.getContentPane().setLayout(new FlowLayout());
jfrm.setSize(500, 160);
table = new JTable(data, headings);
table.setSelectionBackground(Color.GREEN);
TableColumn ledgerColumn = table.getColumnModel().getColumn(0);
ledgerColumn.setCellEditor(new ComboBoxCellEditor(comboBox));
//This is the code that changes the colour of the combo Box when it is selected.
comboBox.getEditor().getEditorComponent().addFocusListener(new FocusListener() {
public void focusGained(FocusEvent arg0) {
comboBox.getEditor().getEditorComponent().setBackground(Color.GREEN);
}
public void focusLost(FocusEvent arg0) {
comboBox.getEditor().getEditorComponent().setBackground(Color.WHITE);
}
});
JScrollPane jscrlp = new JScrollPane(table);
jfrm.getContentPane().add(jscrlp);
jfrm.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable () {
public void run() {
new ComboTest();
}
});
}
}