I would like to make my own method that controles when a component is 'isSelected'.
I have a JList containing multiple JPanel. The constructing class of the JPanel extends ListCellRenderer<>.
To show that one of the JList-component (the JPanels) is selected i use;
#Override
public Component getListCellRendererComponent(..., boolean isSelected, ...) {
if(isSelected){
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
return this;
}
I would like a method that keeps a selected item 'selected' eventhough I choose to select another. I understand this can be done by holding down CTRL, but .setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); does not quite do the trick. I would rather like to select multiple by clicking on them, and deselect by clicking on them.
For this i have worked with the ListSelectionMode, but i cant find a way.
When done the above I would like to implement a method that only selects a component in the list when clicked in a certain area (instead of the whole component which is preset). I have made this method, which returns true if the correct area is clicked, else false. But since I cant figure out how to override the mouseevent that makes the components 'isSelected' this has been tricky.
Here is the code for the method I would like to override the 'isSelected' method;
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent evt) {
if(ActionHandler.mouseClickedPrebuild(evt.getPoint())){
//This code runs if that special place is clicked!
//So now the component should be 'isSelected' or
//deselected if it already was 'isSelected'.
}
}
});
This code is in the constructor of my JList
And the mouseClickedPrebuild method;
public static boolean mouseClickedPrebuild(Point point) {
int index = theJList.locationToIndex(point);
Rectangle bounds = theJList.getCellBounds(index,index);
Point p = bounds.getLocation();
return ( ... long list of greater than & less than ...);
//This gives the certain area which is accepted to return true
I solved the issue!
So I get my view showing by running this line;
// UI Class JScrollPane Custom JList
UIConstructor.listview.setViewportView(new ListView( -insert ArrayList here- ));
Here is my ListView. The custom DefaultListSelectionModel I used to solve my problem was posted by #FuryComptuers right here;
JList - deselect when clicking an already selected item
I had to make a few changes to the code, since the two methods in the selectionModel will run before my mouseevent. I saved the variabels staticly, so instead of running the code in setSelectionInterval I did it inside my mousePressed.
I then could add the boolean isSelected which returns true, if a curtain area within a specific list element is clicked.
public class ListViewd extends JList {
static boolean isSelected;
static Point point;
static boolean gS = false;
static int in0;
static int in1;
#Override
public Dimension getPreferredScrollableViewportSize() {
Dimension size = super.getPreferredScrollableViewportSize();
size.setSize(new Dimension(0,0));
return size;
}
public ListView(ArrayList<System> items) {
DefaultListModel<System> list = new DefaultListModel<System>();
for (System item : items) {
list.addElement(item);
}
this.setSelectionModel(new DefaultListSelectionModel() {
boolean gestureStarted = false;
#Override
public void setSelectionInterval(int index0, int index1) {
gS = gestureStarted;
in0 = index0;
in1 = index1;
gestureStarted = true;
}
#Override
public void setValueIsAdjusting(boolean isAdjusting) {
if (!isAdjusting) {
gestureStarted = false;
}
}
});
ListSelectionModel selectionModel = this.getSelectionModel();
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
point = e.getPoint();
isSelected = ActionHandler.mouseClickedPrebuild(point);
if(!gS && isSelected){
if (isSelectedIndex(in0)) {
selectionModel.removeSelectionInterval(in0, in1);
} else {
selectionModel.addSelectionInterval(in0, in1);
}
}
}
});
setModel(list);
setCellRenderer(new ListModelPrebuild());
}
I've got a Swing screen that opens with a JTable full of itenms and a JList that is empty. After an event from a button I move the itens from JTable to JList, making JTable empty and JList full of items as I desire. But I've got a weird problem that JTable goes back to its original state with all the items again when the frame is minimized and then maximized.
It should not happen as I remove the items from the array that keeps the JTable values. I used sysout to watch on console the array size as the items are being removed and I am sure in the end its size has come to zero.
Plus, I put break points in the getXXX that retrieves the array value to JTable and a repaint() method I put myself to overwrite its original and I got no break point pause of any of them.
So....I don't know where it is getting the value to reset to original state!
Finally, I've just noticed it happens when the table's area is clicked, so change the JFrame to a JDialog where there is no minimize/maximize button would not solve the problem at all.
I don't know if some code would help but in any case, I've got an init method just to initialize the JTable.
private void initTable(Object rowData[][]) {
documents = rowData;
dataModel = new DataModel(rowData, COLUMNS);
scrollPane = new JScrollPane();
scrollPane.setBounds(13, 188, 300, 148);
contentPane.add(scrollPane);
table = new JTable(dataModel) {
#Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
super.changeSelection(rowIndex, columnIndex, true, false);
}
};
scrollPane.setViewportView(table);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getColumnModel().getColumn(0).setPreferredWidth(30);
table.getColumnModel().getColumn(1).setPreferredWidth(260);
table.setCellSelectionEnabled(true);
table.setRowSelectionAllowed(false);
table.setColumnSelectionAllowed(false);
ListSelectionModel cellSelectionModel = table.getSelectionModel();
cellSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
cellSelectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
if (selectedRow >= 0) {
if (selectedColumn == 0) {
Boolean valorCol= (Boolean) documents[selectedRow][0];
if (valorCol== true) {
documents[selectedRow][0] = false;
}
else {
documents[selectedRow][0] = true;
}
}
}
}
table.clearSelection();
}
});
}
_______EDITING_______
The way I remove the items are made of a List not the original array. After the procedure that moves the items from the JTable, I call the method above but this time the list is empty. Here it goes the "missing code"
After moving all the items...
Object[][] documentsMoved = buildTableDataFromList(myVO.getDocuments());
initTable(documentsMoved );
private Object[][] buildTableDataFromList(List<MyVO> list) {
Object[][] retorno = new Object[list.size()][COLUMNS.length];
for (int i = 0; i < list.size(); i++) {
MyVO vo = lista.get(i);
retorno[i][CHECK_COL] = Boolean.TRUE;
retorno[i][DOC_COL] = vo.getFileName();
}
return retorno;
}
It appears you are changing the dataVector of the DataModel directly. This is not the way you should be doing things. What you should do is change cell values through the interface of DataModel (using model indexes) or through the JTable interface (using view indexes).
So my advice would be to change your program to do what I just told. However if you insist on working the way you are you can signal the DataModel about changes in its underlying dataVector, if your DataModel extends DefaultTableModel which I assume it does or implements the AbstractTableModel interface. Call documents.fireTableDataChanged(); after your changes to the whole model, or more granular when changing cells using documents.fireTableCellUpdated(rowModelId,colModelId);.
Another mistake you are making is mixing up view indexes and model indexes. These can be different if rows are sorted and/or columns moved around in your table. Before indexing the model with indexes returned from the view, you should be converting these view indexes with JTable.convertRowIndexToModel and JTable.convertColumnIndexToModel.
Applying this to your selectionListener:
What you should be doing:
cellSelectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
if (selectedRow >= 0) {
if (table.convertColumnIndexToModel(selectedColumn) == 0) {
Boolean valorCol = (Boolean) table.getValueAt(selectedRow,selectedColumn);
if (valorCol == true) {
table.setValueAt(Boolean.FALSE,selectedRow,selectedColumn);
}
else {
table.setValueAt(Boolean.TRUE,selectedRow,selectedColumn);
}
}
}
}
table.clearSelection();
}
});
If you insist on working the way you are:
cellSelectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
if (selectedRow >= 0) {
int rowModelId = table.convertRowIndexToModel(selectedRow);
int colModelId = table.convertColumnIndexToModel(selectedColumn);
if (colModelId == 0) {
Boolean valorCol= (Boolean) documents[rowModelId][0];
if (valorCol== true) {
documents[rowModelId][0] = false;
}
else {
documents[rowModelId][0] = true;
}
dataModel.fireTableCellUpdated(rowModelId,0);
}
}
}
table.clearSelection();
}
});
Alas I think with your understanding of swing and JTable, there are likely more problems in your code.
I have a JTable with custom TableCellRenderer.
public class DateCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 58L;
public DateCellRenderer() {
super();
setHorizontalAlignment(CENTER);
setOpaque(true);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof Date) {
String date = new SimpleDateFormat("dd-MM-yyyy").format((Date) value);
setText(date);
}
return this;
}
}
Also in my application I have a drop down menu by which I can change the look and feel. This drop down menu is in a parent frame and the table is in a dialog. When the dialog is opened the parent frame is inaccessible. So to change the look and feel I have to close the dialog first.
Now in a particular skin if the table is populated by some data and I change the look and feel from parent frame and again open the dialog then the column, where I have added the TableCellRenderer, is keeping the old look and feel. It is not updating while the other columns render themselves in the new look and feel.
I am unable to find the problem and its solution. Any help is appreciable.
Note: The look and feel update of the application is made by the following snippet
javax.swing.UIManager.setLookAndFeel(uiProperties.getThemeModel().getThemeClass());
ComponentFactory.getLibraryFrame().getRootPane().updateUI();
for (int i = 0; i < Frame.getWindows().length; i++) {
SwingUtilities.updateComponentTreeUI(Frame.getWindows()[i]);
}
for (int i = 0; i < Frame.getFrames().length; i++) {
SwingUtilities.updateComponentTreeUI(Frame.getFrames()[i]);
}
Thanks in advance.
In HiFi theme chosen first:
Then I change the theme to Fast, and the second column "Released" not updated its ui:
The JTable is:
public class MovieSearchResultTable extends BaseTable {
private static final long serialVersionUID = 45L;
public MovieSearchResultTable(TableModel tableModel) {
super(tableModel);
LibraryLogger.initMessage(getClass().getSimpleName());
}
#Override
public void initialize() {
setFillsViewportHeight(true);
setAutoResizeMode(AUTO_RESIZE_OFF);
getColumnModel().getColumn(1).setCellRenderer(new DateCellRenderer());//if I comment out this line then no problem. but without CellRenderer how could I format a Date, if I use formatted String instead of Date, then the column will not sort!!
}
#Override
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().getWidth() < getParent().getWidth();
}
}
I think that not good L&F, JTable looks like ... ok, but other Compound JComponents aren't ...., not sure I haven't wasting my time, I leaving to test that, maybe is there something described about that on their Forum or Documentation or BugParades, but nothing from your question
there is very simple way and you can any time to check that
1) go to Insubstantial
2) download code source,
3) import all classes to the IDE (2-15 min depends of PC HardWare)
4) search for folder test, there is Check.java,
5) run that and to try everything in JMenu Look and Feel, before that required to download API's for every Custom Java Swing Look and Feels
The solution, you need to override public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
Here is the class:
public class MovieSearchResultTable extends BaseTable {
private static final long serialVersionUID = 45L;
private int rolloverRowIndex = -1;
public MovieSearchResultTable(TableModel tableModel) {
super(tableModel);
LibraryLogger.initMessage(getClass().getSimpleName());
}
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component component = super.prepareRenderer(renderer, row, column);
Color foreground = getForeground();
Color background = getBackground();
if (isRowSelected(row)) {
foreground = getSelectionForeground();
background = getSelectionBackground();
}
else if (row == rolloverRowIndex) {
foreground = getSelectionForeground();
background = ColorHelper.brighter(getSelectionBackground(), 40);
}
else if (row % 2 == 0) {
background = ColorHelper.brighter(getParent().getBackground(), 20);
}
component.setForeground(foreground);
component.setBackground(background);
return component;
}
private class RolloverListener extends MouseInputAdapter {
public void mouseExited(MouseEvent e) {
rolloverRowIndex = -1;
repaint();
}
public void mouseMoved(MouseEvent e) {
int row = rowAtPoint(e.getPoint());
if (row != rolloverRowIndex) {
rolloverRowIndex = row;
repaint();
}
}
}
#Override
public void initialize() {
setFillsViewportHeight(true);
setAutoResizeMode(AUTO_RESIZE_OFF);
TableColumnModel tableColumnModel = getColumnModel();
for(ComponentConstant.ColumnName columnName : ComponentConstant.Column.MOVIE_SEARCH_RESULT_TABLE) {
int order = columnName.getOrder();
TableColumn tableColumn = tableColumnModel.getColumn(order);
if(order == 0) {
continue;
}
tableColumn.setCellRenderer(RendererFactory.getMovieSearchResultTableCellRenderer());
}
RolloverListener listener = new RolloverListener();
addMouseMotionListener(listener);
addMouseListener(listener);
}
#Override
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().getWidth() < getParent().getWidth();
}
}
Thanks.
I have a JTable which is created with the use of an EventTableModel and is in a JScrollPane. The EventTableModel takes live updates from an eventList and displays the result in the table. As new results come into the table and the new piece of information is displayed at the top of the table.
However, what I want to be able to do is freeze the table to show what is currently displayed when I press a button called 'Lock Table'. This button should have the same effect as the eclipse console 'Scroll Lock', therefore as new items appear the current items should remain on the screen and not be pushed off as new items appear. But new items should still be added just not automatically scrolled to.
Does anyone know how I can try achieve this functionality. So that as update come in, the data that is on the table is not forced off screen, therefore focuses remain on the current data when the check box is pressed.
Thanks for any help.
Michael
Basic procedure (for inserting above the current display area)
install a TableModelListener on the table's model
on enable lock: note the number of rows below the current visible rectangle
on receiving inserts while locked, scroll so that the number of rows below are kept constant
some working code (using JXTable, as it has convenience method for scrolling, for a core table simply do the calculations yourself :-)
public static class ScrollLock {
private JXTable table;
private boolean blocked;
private int rowsBelow;
public ScrollLock(JXTable table) {
this.table = table;
table.getModel().addTableModelListener(getTableModelListener());
}
private TableModelListener getTableModelListener() {
TableModelListener l = new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (!blocked) return;
if (e.getType() == TableModelEvent.INSERT) {
updateInsert(e.getFirstRow(), e.getLastRow());
}
}
};
return l;
}
protected void updateInsert(int firstRow, int lastRow) {
// PENDING: assumption is that insert always above
// need additional logic for other cases
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Rectangle r = table.getVisibleRect();
int row =table.rowAtPoint(new Point(0, r.y + r.height));
int lastVisible = table.getRowCount() - rowsBelow;
table.scrollRowToVisible(lastVisible);
}
});
}
public void block() {
Rectangle viewRect = table.getVisibleRect();
int lastVisibleRow = table.rowAtPoint(new Point(0, viewRect.y + viewRect.height));
rowsBelow = table.getRowCount() - lastVisibleRow;
blocked = true;
}
public void unblock() {
blocked = false;
rowsBelow = -1;
}
}
What would be the best way to have a list of items with a checkbox each in Java Swing?
I.e. a JList with items that have some text and a checkbox each?
A wonderful answer is this CheckBoxList. It implements Telcontar's answer (though 3 years before :)... I'm using it in Java 1.6 with no problems. I've also added an addCheckbox method like this (surely could be shorter, haven't used Java in a while):
public void addCheckbox(JCheckBox checkBox) {
ListModel currentList = this.getModel();
JCheckBox[] newList = new JCheckBox[currentList.getSize() + 1];
for (int i = 0; i < currentList.getSize(); i++) {
newList[i] = (JCheckBox) currentList.getElementAt(i);
}
newList[newList.length - 1] = checkBox;
setListData(newList);
}
I tried out the demo for the Jidesoft stuff, playing with the CheckBoxList I encountered some problems (behaviors that didn't work). I'll modify this answer if I find problems with the CheckBoxList I linked to.
Create a custom ListCellRenderer and asign it to the JList.
This custom ListCellRenderer must return a JCheckbox in the implementantion of getListCellRendererComponent(...) method.
But this JCheckbox will not be editable, is a simple paint in the screen is up to you to choose when this JCheckbox must be 'ticked' or not,
For example, show it ticked when the row is selected (parameter isSelected), but this way the check status will no be mantained if the selection changes. Its better to show it checked consulting the data below the ListModel, but then is up to you to implement the method who changes the check status of the data, and notify the change to the JList to be repainted.
I Will post sample code later if you need it
ListCellRenderer
Just implement a ListCellRenderer
public class CheckboxListCellRenderer extends JCheckBox implements ListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
setComponentOrientation(list.getComponentOrientation());
setFont(list.getFont());
setBackground(list.getBackground());
setForeground(list.getForeground());
setSelected(isSelected);
setEnabled(list.isEnabled());
setText(value == null ? "" : value.toString());
return this;
}
}
and set the renderer
JList list = new JList();
list.setCellRenderer(new CheckboxListCellRenderer());
this will result in
Details at Custom swing component renderers.
PS: If you want radio elements just replace extends JCheckbox with extends JRadioButton.
I'd probably be looking to use a JTable rather than a JList and since the default rendering of a checkbox is rather ugly, I'd probably be looking to drop in a custom TableModel, CellRenderer and CellEditor to represent a boolean value. Of course, I would imagine this has been done a bajillion times already. Sun has good examples.
Better solution for Java 7 and newer
I stumbled upon this question and realized that some of the answers are pretty old and outdated. Nowadays, JList is generic and thus there are better solutions.
My solution of the generic JCheckBoxList:
import java.awt.Component;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
#SuppressWarnings("serial")
public class JCheckBoxList extends JList<JCheckBox> {
protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
public JCheckBoxList() {
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int index = locationToIndex(e.getPoint());
if (index != -1) {
JCheckBox checkbox = (JCheckBox) getModel().getElementAt(index);
checkbox.setSelected(!checkbox.isSelected());
repaint();
}
}
});
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
public JCheckBoxList(ListModel<JCheckBox> model){
this();
setModel(model);
}
protected class CellRenderer implements ListCellRenderer<JCheckBox> {
public Component getListCellRendererComponent(
JList<? extends JCheckBox> list, JCheckBox value, int index,
boolean isSelected, boolean cellHasFocus) {
JCheckBox checkbox = value;
//Drawing checkbox, change the appearance here
checkbox.setBackground(isSelected ? getSelectionBackground()
: getBackground());
checkbox.setForeground(isSelected ? getSelectionForeground()
: getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ? UIManager
.getBorder("List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}
For dynamically adding JCheckBox lists you need to create your own ListModel or add the DefaultListModel.
DefaultListModel<JCheckBox> model = new DefaultListModel<JCheckBox>();
JCheckBoxList checkBoxList = new JCheckBoxList(model);
The DefaultListModel are generic and thus you can use methods specified by JAVA 7 API here like this:
model.addElement(new JCheckBox("Checkbox1"));
model.addElement(new JCheckBox("Checkbox2"));
model.addElement(new JCheckBox("Checkbox3"));
I recommend you use a JPanel with a GridLayout of 1 column. Add the checkBoxes to the JPanel, and set the JPanel as the data source of a JScrollPane. And to get the selected CheckBoxes, just call the getComponents() of the JPanel to get the CheckBoxes.
Odds are good w/ Java that someone has already implemented the widget or utility you need. Part of the benefits of a large OSS community. No need to reinvent the wheel unless you really want to do it yourself. In this case it would be a good learning exercise in CellRenderers and Editors.
My project has had great success with JIDE. The component you want, a Check Box List, is in the JIDE Common Layer (which is OSS and hosted on java.net). The commercial stuff is good too, but you don't need it.
http://www.jidesoft.com/products/oss.htm
https://jide-oss.dev.java.net/
I don't like the solutions that put a Checkbox into the model. The model should only contain data not display elements.
I found this http://www.java2s.com/Tutorials/Java/Swing_How_to/JList/Create_JList_of_CheckBox.htm
which I optimized a bit. The ACTIVE flag represents the Checkbox, the SELECTED flag shows what entry the cursor sits on.
my version requires a renderer
import java.awt.Component;
import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
class CheckListRenderer extends JCheckBox implements ListCellRenderer<Entity> {
#Override
public Component getListCellRendererComponent(JList<? extends Entity> list,
Entity value, int index, boolean isSelected, boolean cellHasFocus) {
setEnabled(list.isEnabled());
setSelected(value.isActive()); // sets the checkbox
setFont(list.getFont());
if (isSelected) { // highlights the currently selected entry
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setText(value.toString()+" - A" + value.isActive()+" - F"+cellHasFocus+" - S"+isSelected );
return this;
}
}
and an entity that got the active field:
public class Entity {
private boolean active = true;
public boolean isActive() {
return active;
}
public void setActive(boolean isActive) {
this.active = isActive;
}
}
Now you only have to add this to your JList:
list = new JList<Entity>();
list.setModel(new DefaultListModel<Entity>());
list.setCellRenderer(new CheckListRenderer());
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent event) {
if (event.getX() < 20) {
// Quick and dirty: only change the tick if clicked into the leftmost pixels
#SuppressWarnings("unchecked")
JList<Entity> list = ((JList<Entity>) event.getSource());
int index = list.locationToIndex(event.getPoint());// Get index of item clicked
if (index >= 0) {
Entity item = (Entity) list.getModel().getElementAt(index);
item.setActive(!item.isActive()); // Toggle selected state
list.repaint(list.getCellBounds(index, index));// Repaint cell
}
}
}
});
All of the aggregate components in Swing--that is, components made up other components, such as JTable, JTree, or JComboBox--can be highly customized. For example, a JTable component normally displays a grid of JLabel components, but it can also display JButtons, JTextFields, or even other JTables. Getting these aggregate components to display non-default objects is the easy part, however. Making them respond properly to keyboard and mouse events is a much harder task, due to Swing's separation of components into "renderers" and "editors." This separation was (in my opinion) a poor design choice and only serves to complicate matters when trying to extend Swing components.
To see what I mean, try enhancing Swing's JList component so that it displays checkboxes instead of labels. According to Swing philosophy, this task requires implementing two interfaces: ListCellRenderer (for drawing the checkboxes) and CellEditor (for handling keyboard and mouse events on the checkboxes). Implementing the ListCellRenderer interface is easy enough, but the CellEditor interface can be rather clumsy and hard to understand. In this particular case, I would suggest forgetting CellEditor entirely and to handle input events directly, as shown in the following code.
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
public class CheckBoxList extends JList
{
protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
public CheckBoxList()
{
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
int index = locationToIndex(e.getPoint());
if (index != -1) {
JCheckBox checkbox = (JCheckBox)
getModel().getElementAt(index);
checkbox.setSelected(
!checkbox.isSelected());
repaint();
}
}
}
);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
protected class CellRenderer implements ListCellRenderer
{
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus)
{
JCheckBox checkbox = (JCheckBox) value;
checkbox.setBackground(isSelected ?
getSelectionBackground() : getBackground());
checkbox.setForeground(isSelected ?
getSelectionForeground() : getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ?
UIManager.getBorder(
"List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}
Here, I intercept mouse clicks from the listbox and simulate a click on the appropriate checkbox. The result is a "CheckBoxList" component that is both simpler and smaller than an equivalent component using the CellEditor interface. To use the class, simply instantiate it, then pass it an array of JCheckBox objects (or subclasses of JCheckBox objects) by calling setListData. Note that the checkboxes in this component will not respond to keypresses (i.e. the spacebar), but you could always add your own key listener if needed.
Source: DevX.com
Here is just a little addition to the JCheckBoxList by Rawa. This will add the ability to select using space bar. If multiple items are selected, all will be set to inverted value of the first item.
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
int index = getSelectedIndex();
if (index != -1 && e.getKeyCode() == KeyEvent.VK_SPACE) {
boolean newVal = !((JCheckBox) (getModel()
.getElementAt(index))).isSelected();
for (int i : getSelectedIndices()) {
JCheckBox checkbox = (JCheckBox) getModel()
.getElementAt(i);
checkbox.setSelected(newVal);
repaint();
}
}
}
});
this is yet another example of making list with checkboxes
class JCheckList<T> extends JList<T> {
protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
public void setSelected(int index) {
if (index != -1) {
JCheckBox checkbox = (JCheckBox) getModel().getElementAt(index);
checkbox.setSelected(
!checkbox.isSelected());
repaint();
}
}
protected static class CellListener
extends DefaultListModel
implements ListDataListener {
ListModel ls;
public CellListener(ListModel ls) {
ls.addListDataListener(this);
int i = ls.getSize();
for (int v = 0; v < i; v++) {
var r = new JCheckBox();
r.setText(ls.getElementAt(v).toString());
this.addElement(r);
}
this.ls = ls;
}
#Override
public void intervalAdded(ListDataEvent e) {
int begin = e.getIndex0();
int end = e.getIndex1();
for (; begin <= end; begin++) {
var r = new JCheckBox();
r.setText(ls.getElementAt(begin).toString());
this.add(begin, r);
}
}
#Override
public void intervalRemoved(ListDataEvent e) {
int begin = e.getIndex0();
int end = e.getIndex1();
for (; begin <= end; end--) {
this.remove(begin);
}
}
#Override
public void contentsChanged(ListDataEvent e) {
}
}
public JCheckList() {
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int index = locationToIndex(e.getPoint());
setSelected(index);
}
}
);
addKeyListener(new KeyListener(){
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE){
int index = JCheckList.this.getSelectedIndex();
setSelected(index);
}
}
#Override
public void keyReleased(KeyEvent e) {
}
});
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
#Override
public void setModel(ListModel<T> d) {
var r = new CellListener(d);
d.addListDataListener(r);
super.setModel(r);
}
protected class CellRenderer implements ListCellRenderer {
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
JCheckBox checkbox = (JCheckBox) value;
checkbox.setBackground(isSelected
? getSelectionBackground() : getBackground());
checkbox.setForeground(isSelected
? getSelectionForeground() : getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected
? UIManager.getBorder(
"List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}