How to add a custom checkbox into JList (Java)? - java

This is how the program looks:
This is how I want it to look:
As you can see in the picture I have tried a bit and learned that I need to use ListCellRenderer, but the problem is i have created 2 custom png files
checked.png and
unchecked.png
when I click daily goals #1 it should give state = true and checked.png should appear and stay checked unless I click it again. Unchecked.png could be standard on the jList column.
I also want to place my checkbox 1 cm to the left of the end of the row (padding) not sure hows its done in java sadly. (You'll understand better by looking at the picture)
After looking through some guides I have learned that the only way to add extra stuff to a JList column is by using ListCellRenderer. I have tried quite a while with no success so thought of asking others. Does anyone have any ideas on how to do this?
The thought was to get it to work then display in a JTable by changing the Jtable column to Daily goals and displaying X to indicate the goal was achieved. But I think I should be able to do this, The main question is the custom checkbox implementation.

You can have two types of checkboxes to be used as jlist cell renderers, one for selected cells, another for unselected.
Use ImageIcon to decorate the checkbox with your images.
In your jlist cell render you need to have logic to return the intended checkbox to render that list cell.
Note to override the text in the checkbox to the actual list cell value
public class TestFrame extends JFrame {
ImageIcon iconChecked = new ImageIcon(TestFrame.class.getResource("checked.png"));
ImageIcon iconUnchecked = new ImageIcon(TestFrame.class.getResource("unchecked.png"));
JList jList = new JList(new Object[]{"ABC", "123"});
public TestFrame() {
this.add(jList);
jList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
jList.setCellRenderer(new ListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
for (int i : list.getSelectedIndices()) {
if (index == i) {
JCheckBox checkBoxChecked = new JCheckBox(value.toString(), iconChecked);
return checkBoxChecked;
}
}
JCheckBox checkBoxUnchecked = new JCheckBox(value.toString(), iconUnchecked);
return checkBoxUnchecked;
}
});
}}

Related

Repaint Table issue with Swing JTable

I am working with swing JTable and have a trouble with repainting table. I draw a JTable with thr following code
Object[] column = new Object[]{"Entity", "Attribute"};
Object[][] rowData = new Object[][]{{"E1", "A1"},{"E2", "A2"}};
TableCellRenderer cellRenderer = new TableCellRenderer();
JTable table = new JTable(new DefaultTableModel(rowData, column));
table.setCellSelectionEnabled(true);
table.getColumnModel().getColumn(0).setCellRenderer(cellRenderer);
Below is my table renderer code ..
public class TableCellRenderer implements javax.swing.table.TableCellRenderer {
//private JPanel panel;
JTextField field;
private JTable table;
#Override
public Component getTableCellRendererComponent(final JTable table, final Object value,
boolean isSelected, boolean hasFocus, final int row, final int column) {
this.table = table;
//JTextField field = null;
System.out.println("Rendere : Row : " + row + "Column : " + column);
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JButton button = new JButton("?");
button.setPreferredSize(new Dimension(20, panel.getHeight()));
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
new SelectionDialog(panel, table, value, row, column);
}
});
if(table.getValueAt(row, column) != null){
field = new JTextField(table.getValueAt(row, column).toString());
}else{
field = new JTextField(table.getValueAt(row, column).toString());
}
field.setPreferredSize(new Dimension(30, panel.getHeight()));
panel.add(field, BorderLayout.WEST);
panel.add(button, BorderLayout.EAST);
return panel;
}
this is how i am updating contents of a cell in table..
SelectionDialog.this.table.getModel().setValueAt("E7", 0, 0);
I am updating the table model data via the SelectionDialog for e.g change data at row 0, colum 0 to E7 etc. After changing data in table model i have tried the following options but none of them has refreshed the table data in view however the data in model of JTable was updated correctly. If I add a new row on the fly and then call the below methods then every thing work fine but if I modify data in the model of an existing row then none of the solution mentioned below is working
//((DefaultTableModel)SelectionDialog.this.table.getModel()).addRow(new Object[]{"E3", "A3"});
//((DefaultTableModel)SelectionDialog.this.table.getModel()).fireTableCellUpdated(SelectionDialog.this.row, SelectionDialog.this.column);
//((DefaultTableModel)SelectionDialog.this.table.getModel()).fireTableChanged(new TableModelEvent(SelectionDialog.this.table.getModel()));
//((DefaultTableModel)SelectionDialog.this.table.getModel()).fireTableStructureChanged();
//SelectionDialog.this.table.repaint();
// SelectionDialog.this.table.revalidate();
Please provide any insights about the problem as I am to swing and may have missed some very prominent things.
Edit 1: Just adding one more note which i wanted to place earlier but don't know how i missed. Table is not updated in general but if i focus out from the cell in which change was made or i change the size of table then it immediately change the contents of that particular cell to fresh selected value.
Problem Solved:
I am placing my findings for someone who is facing similar problem.
I rendered a button and a text box inside each cell in my table. When button was clicked (Editor code is not provided as it looks irrelevant to me to place here) a dialog appear which inputs value from user and update the specific column and row.
The lines i mentioned as not working at the end of my post (before edit 1) were working correctly however renderer was not executing unless i manually focus out from the selected cell (whose button was clicked) or manually change the size of jtable which make sense as button was inside editor and button click shows that cell is edited and off-course renderer will not execute unless editing is finished which requires focus out or enter key etc.
I applied the following code as
table.editCellAt(-1, -1);
It focus out from the edited cell (edited with the button) and hence renderer executes and work as expected.
When you are using a DefaultTableModel and you want to update a certain value, you need to use the DefaultTableModel#setValueAt method.
Calling this method (on the Event Dispatch Thread of course) will update your model and fire the necessary events. Those events will cause your JTable to update itself.
A few additional remarks about your renderer:
Creating new components in each call of your renderer is not the way to go. This becomes incredibly slow for large tables. Create all components once in the constructor of your renderer, and just update their state
Adding a JButton to your table in the renderer has no effect, unless you have an editor as well. The button will not be clickable, and the action listener you attach to it will never be called. See the renderers and editors section in the JTable tutorial for more information.
There should be no need to call getValueAt in your renderer. The value is passed in as one of the arguments

JTable JComboBox is defaulting to display 1st item when list is expanded

I have been trying to determine why my JComboBox is displaying the 1st item in the list through numerous Google searches, but I'm struggling to find relevant help. It could be that I don't know the correct terminology (hence the overly specific title of this question) and thus not finding the information that would explain my issue. I checked out the JComboBox API, and few of the listeners and models that it uses, but they did not seem likely candidates.
The JComboBox in question is inside a JTable, so I am not aware if that changes the default behaviour of it. The code I am using is as below:
//row and col are final due to usage inside anonymous inner class
public TableCellEditor getCellEditor(final int row, final int col)
{
String[] listItems = new String[arrayList.getSize()];
int i = -1;
for(String s : arrayList)
{
i++;
listItems[i] = s;
}
JComboBox<String> box = new JComboBox<>(listItems);
box.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent e)
{
if(e.getStateChange() == ItemEvent.SELECTED)
{
if(e.getItem().equals("Add/Edit Projectile"))
{
//Where Editor is a JFrame that will be opened
new Editor();
}
}
}
});
DefaultCellEditor list = new DefaultCellEditor(box);
}
Please note that the Arraylist in my program does not contain Strings, but instead a more complicated set of custom objects that I believe would distract from the main issue.
I haven't included a Renderer for JComboBox's in the JTable as I was happy enough with the way it appeared, and figured that my problem was more going to be something I have neglected to implement in the model/implemented wrong.
I've also provided a couple of screenshots to better portray my problem. The first image is when the JComboBox is not selected, and simply displaying the currently selected item.
The second image is when I have just clicked the JComboBox to bring up the list. As depicted, it will immediately bring up that first item, no matter what it is.
If anyone has any suggestions as to where to look/solutions, I would be very grateful.
EDIT
My particular table has two columns, where the left column is a variable name, and the right column is the value associated with the variable. The tables role is to display the properties of a selected object, where each value for different variable for different objects are likely to not be the same.
In this particular case, the cell displays a JComboBox with all the available Projectiles in the game we are making. Each enemy has a different type of projectile it defaults to. So when I click on a different enemy in our game area, the table will display all of their current properties (defaults if they have not been changed).
Enemies do have a getter for the Projectile, so I could determine what the currently selected enemy is, get it's projectile, do a toString() to find how it is to be represented in the list, and do a setValueAt().
The only problem is at the moment it is always selecting the first item in the list when the list is expanded.
Unless the values for the JComboBox are dynamically generated for each row, you should be able to just prepare the CellEditor ahead of time, for example...
JComboBox cb = new JComboBox(new String[]{"1", "2", "3", "4"});
DefaultCellEditor editor = new DefaultCellEditor(cb);
JTable table = new JTable(new DefaultTableModel(5, 1));
table.getColumnModel().getColumn(0).setCellEditor(editor);
This will set the selected value of the editor to the value of the cell when the editing process starts
Updated
In the case where the combobox values are dynamically generate per row, you could do something more like...
JComboBox cb = new JComboBox();
DefaultCellEditor editor = new DefaultCellEditor(cb) {
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
JComboBox editor = (JComboBox) getComponent();
String[] listItems = new String[arrayList.getSize()];
int i = -1;
for (String s : arrayList) {
i++;
listItems[i] = s;
}
DefaultComboBoxModel model = new DefaultComboBoxModel(listItems);
editor.setModel(model);
editor.setSelectedItem(value);
return editor;
}
};
JTable table = new JTable(new DefaultTableModel(5, 1));
table.getColumnModel().getColumn(0).setCellEditor(editor);
Note the use of editor.setSelectedItem(value);, this will set the selected value to the cells current value...
You could also re-use the model, clearing it each time and re-filling it with new values. You might find this more efficient if you have a large number of rows as you won't need to constantly create a new model each time a cell is edited
Thow this is an oldie...
Your problem is most likely you don't implement "equals" in the class used in the combo.
The Combo needs to select the current item when it is being prepared and does so by iterating through the elements of the model and selects the first one that is equal to the value in the cell. If none is encountered then it leaves the combo as is (either first element or the last used element in a previous cell edit)
This is how you should default to the previously selected element:
//...
Object selectedItem = box.getSelectedItem();
//Add some elements to the jComboBox
box.setSelectedItem(selectedItem);

sorting arrows jtable column header

Does anyone know how to implement the up and down arrows of a JTable column header while sorting its rows?
I have made my own way of sorting and it is triggered by listening to mouse clicks by mouseadapter and the only things that is left is the visibility of such arrows on the header...
Is there also a way to easily implement a sortable jtable?
I finished doing all the sorting and one last thing that i can't do is show the sorting arrows..
i don't want to make a new one but i failed to find if there is an setEnableArrow or something..
any ideas about this?
You can take a look in the source code of the DefaultTableCellHeaderRenderer. In the getTableCellRendererComponent you see from where those icons are retrieved (e.g. UIManager.getIcon("Table.ascendingSortIcon")) and how this icon is set (setIcon(sortIcon);)
I suggest you don't mess up with the DefaultTableCellHeaderRenderer. The problem with this one, is that it's just that, the default. Each LaF is supposed to create a subclass of this one and do its own rendering there. My suggestion is to use a LaF that provides this functionality out of the box. I think that TinyLaf can do this but I'm not sure. You can subclass DefaultTableCellHeaderRenderer but you risk alienating the header rendering from the rest of the LaF.
So how to do it? Unicode to the rescue! Refer to the geometric shapes page and use what you like. I picked the '\u25B2' and '\u25BC' triangles. But then I had to hide the dreaded Swing icon:
UIManager.put( "Table.ascendingSortIcon", new EmptyIcon() );
UIManager.put( "Table.descendingSortIcon", new EmptyIcon() );
Be very careful with the above lines! They will replace the icons for all JTables in your application which might not be what you want. Then you should be able to see something like that:
Empty Icon can be like:
class EmptyIcon implements Icon, Serializable {
int width = 0;
int height = 0;
public void paintIcon(Component c, Graphics g, int x, int y) {}
public int getIconWidth() { return width; }
public int getIconHeight() { return height; }
}
This is the easiest way to implement sorting:
MyModel model = new MyModel();
TableRowSorter<MyModel> sorter = new TableRowSorter<MyModel> (model);
jTable1 = new javax.swing.JTable();
jTable1.setRowSorter(sorter);
jTable1.setModel(model);
What if you use JXtable instead of a Jtable?
these tables have the arrows in the header to sort them and they are easy to use...
worth trying...

How to get a handle to all JCheckBox objects in order to loop?

I'm very new to Java and am having some issues looping through JCheckBoxes on a UI. The idea is that I have a bunch of checkboxes (not in a group because more than one can be selected.) When I click a JButton, I want to build a string containing the text from each selected checkbox. The issue I'm having is that our instructor told us that the checkboxes need to be created via a method, which means (see code below) that there isn't a discrete instance name for each checkbox. If there were, I could say something like
if(checkBox1.isSelected()) {
myString.append(checkBox.getText());
}
That would repeat for checkBox2, checkBox3, and so on. But the method provided to us for adding checkboxes to a panel looks like this:
public class CheckBoxPanel extends JPanel {
private static final long serialVersionUID = 1L;
public CheckBoxPanel(String title, String... options) {
setBorder(BorderFactory.createTitledBorder(BorderFactory
.createEtchedBorder(), title));
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
// make one checkbox for each option
for (String option : options) {
JCheckBox b = new JCheckBox(option);
b.setActionCommand(option);
add(b);
}
}
}
This is called like this:
toppingPanel = new CheckBoxPanel("Each Topping $1.50", "Tomato", "Green Pepper",
"Black Olives", "Mushrooms", "Extra Cheese",
"Pepperoni", "Sausage");
So I now have a panel that contains a border with the title "Each Topping $1.50", and 7 visible checkboxes. What I need to do is get a list of all the selected toppings. We are not supposed to use an ActionListener for each checkbox, but rather get the list when a button is clicked. I'm feeling really clueless here, but I just can't figure out how to get the isSelected property of the checkboxes when the individual checkboxes don't have instance names.
Ideally I'd like to somehow add all the checkboxes to an array and loop through the array in the button's action listener to determine which ones are checked, but if I have to check each one individually I will. I just can't figure out how to refer to an individual checkbox when they've been created dynamically.
I'm assuming you're not allowed to alter the CheckBoxPanel code at all. Which seems like a useless exercise, because in the real world, you'd think that if CheckBoxPanel where a class being provided to you (e.g. in a library) it would include a way of getting the selected options. Anyway, due to the limitation, you could do something like this:
for( int i=0; i<checkBoxPanel.getComponentCount(); i++ ) {
JCheckBox checkBox = (JCheckBox)checkBoxPanel.getComponent( i );
if( checkBox.isSelected() ) {
String option = checkBox.getText();
// append text, etc
}
}
I suggest you maintain a list of checkboxes:
List<JCheckBox> checkboxes = new ArrayList<JCheckBox>();
and before add(b) do:
checkboxes.add(b);
You may then iterate through the list of checkboxes in the buttons action-code using a "for-each" loop construct:
for (JCheckBox cb : checkboxes)
if (cb.isSelected())
process(cb.getText()); // or whatever.
Alternatively, if you need to keep track of the specific index:
for (int i = 0; i < checkboxes.size(); i++)
if (checkboxes.get(i).isSelected())
....
I would suggest that you dont put each of the checkboxes in a List when you create them. Instead, in your shared ActionListener, you maintain a Set of all selected checkboxes. Use the getSource method on the ActionEvent to identify which checkbox the user selected and then cast it to a JCheckBox. If isSelected() returns true for the item in question, attempt to add it to your selectedItems Set. If it is not, then attempt to remove it.
You can then just iterate over the subset of all items (only those that are selected) and print them to the console.

Swing: Positioning a popup from within a JScrollPane

I have a JTable inside of a JScrollPane. I am creating a custom cell editor for one of the columns of the table, and I want this editor to pop up a scrolling JList. I've done this by using a Popup to show a new JScrollPane containing the JList.
Everything is working, except for the position of the Popup. My custom component for the editor looks basically like this:
public class CustomPanel extends JPanel {
JTextField text = new JTextField();
JList list = new JList();
JScrollPane scroll = new JScrollPane(list);
Component owner = null;
public CustomPanel(Component owner) {
this.owner = owner;
add(text);
}
public void showPopup() {
Popup p = PopupFactory.getPopup(owner, scroll, getX(), getY()+getHeight());
p.show();
}
}
What is happening is that getX() and getY() are returning the position of the table cell relative to the JScrollPane holding it, and Popup is wanting absolute screen position. Even if I pass in owner the JScrollPane that they are relative to, it doesn't work. I get the same problem if I use text.getX() / text.getY().
How can I position my Popup directly below the TextBox?
Just a bit more background: The end goal is a multiple-select combobox that displays all of the selected items as a comma-separated list. If something else like this already exists, please don't hesitate to point me to it.
Edit: owner.getLocationOnScreen().y + getY() doesn't work when the scroll pane is anywhere but scrolled all the way up. However, just plain getLocationOnScreen().y DOES work. Problem solved, thank you.
You can query the absolute screen position with Component.getLocationOnScreen(). Is that what you're looking for?

Categories

Resources