How to sort the jComboBox elements list into sorted list.
JComboBox box=new JComboBox();
box.addItem("abc");
box.addItem("zzz");
box.addItem("ccc");
add(box);
i used many jComboBox Components but it's not working.
How to sort this list into ascending order?
You can have a look at the SortedComboBoxModel.
This model extends the DefaultComboBoxModel and has two additional
pieces of functionality built into it:
upon creation of the model, the supplied data will be sorted before
the data is added to the model when adding new items to the model, the
items will be inserted so as to maintain the sort order
The default sort order will be the natural sort order of the items
added to the model. However, you can control this by specifying a
custom Comparator as a parameter of the constructor.
Here's an example how to use it (taken from there):
String[] items = { "one", "two", "three" };
SortedComboBoxModel<String> model = new SortedComboBoxModel<String>(items);
JComboBox<String> comboBox = new JComboBox<String>(model);
comboBox.addItem("four");
comboBox.addItem("five");
comboBox.setSelectedItem("one");
Source code
You can override the default behavior of addItem to suit your needs.
Runnable Example
public class SortedCombobox {
#SuppressWarnings("serial")
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Overriden JCombobox");
frame.getContentPane().setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComboBox box = new JComboBox(){
#Override public void addItem(Object obj){
int count = getItemCount();
String toAdd = (String) obj;
List<String> items = new ArrayList<String>();
for(int i = 0; i < count; i++){
items.add((String)getItemAt(i));
}
if(items.size() == 0){
super.addItem(toAdd);
return;
}else{
if(toAdd.compareTo(items.get(0)) <= 0){
insertItemAt(toAdd, 0);
}else{
int lastIndexOfHigherNum = 0;
for(int i = 0; i < count; i++){
if(toAdd.compareTo(items.get(i)) > 0){
lastIndexOfHigherNum = i;
}
}
insertItemAt(toAdd, lastIndexOfHigherNum+1);
}
}
}
};
box.addItem("zzz");
box.addItem("hh");
box.addItem("aa");
box.addItem("yy");
box.addItem("uu");
box.addItem("bb");
box.addItem("rr");
box.addItem("aa");
box.setSelectedIndex(0);
frame.getContentPane().add(box);
frame.pack();
frame.setVisible(true);
}
});
}
}
The SortedComboBoxModel link from Alexis C. does not appear to work anymore, although the source link still works.
Nonetheless, I created a SortedComboBoxModel for classes that implement Comparable (based on this example).
public class SortedComboBoxModel<E extends Comparable<? super E>> extends DefaultComboBoxModel<E> {
public SortedComboBoxModel() {
super();
}
public SortedComboBoxModel(E[] items) {
Arrays.sort(items);
int size = items.length;
for (int i = 0; i < size; i++) {
super.addElement(items[i]);
}
setSelectedItem(items[0]);
}
public SortedComboBoxModel(Vector<E> items) {
Collections.sort(items);
int size = items.size();
for (int i = 0; i < size; i++) {
super.addElement(items.elementAt(i));
}
setSelectedItem(items.elementAt(0));
}
#Override
public void addElement(E element) {
insertElementAt(element, 0);
}
#Override
public void insertElementAt(E element, int index) {
int size = getSize();
for (index = 0; index < size; index++) {
Comparable c = (Comparable) getElementAt(index);
if (c.compareTo(element) > 0) {
break;
}
}
super.insertElementAt(element, index);
}
}
This can be used like so:
public static void main(String[] args) {
javax.swing.JComboBox<String> sortedComboBox = new javax.swing.JComboBox<>();
String[] testArray = new String[]{"DDD", "AAA", "CCC", "BBB"};
sortedComboBox.setModel(new SortedComboBoxModel<>(testArray));
//print out the sorted contents
for (int i = 0; i < sortedComboBox.getItemCount(); i++) {
System.out.println(i + ": " + sortedComboBox.getItemAt(i));
}
}
Related
I have a Bubble Sort algorithm which I want to visualize with SWING in a JFrame. It goes well, if I call the function in a standalone position, but doesn't if I call it from a JButton's ActionListener. (Actually it works, only it doesn't render the array continously - as it should and does in the other case - only in the beginning and the end of the sorting.) Any idea why?
GUI and Main class:
public class sortings {
public static void main(String[] args) {
//create, fill up and print array of 100 elements
int[] arr = new int[100];
for (int i=0; i<100; i++) {
int element = ThreadLocalRandom.current().nextInt(1, 100);
arr[i] = element;
}
for (int i=0; i<arr.length; i++) {
System.out.print(arr[i] + ",");
}
//set up GUI
Frame frame = new Frame();
JPanel menuPanel = new JPanel();
JButton button = new JButton("sort");
Container contentPane = frame.getContentPane();
contentPane.setPreferredSize(new Dimension(600, 450));
frame.setLayout(new GridLayout(2,1));
menuPanel.setSize(600, 50);
menuPanel.add(button);
BubbleSort bs = new BubbleSort(arr);
frame.add(menuPanel);
frame.add(bs);
frame.pack();
button.addActionListener((ActionEvent e) -> {
bs.bubbleSort(); // <- if called from here it renders the array only in the beginning and the end of sorting
});
bs.bubbleSort(); // <- if called from here it renders the array during sorting continously, as it should
//BubbleSort bs = new BubbleSort(arr);
//bs.bubbleSort();
}
}
BubbleSort class and method:
public class BubbleSort extends JPanel {
int[] arr;
public BubbleSort(int[] arr) {
setSize(600, 400);
this.arr = arr;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < this.arr.length; i++) {
g.drawLine(i*2+50,50, i*2+50, 50+this.arr[i]);
}
}
public void bubbleSort() {
int temp;
for (int i=0; i < 100; i++) {
for (int j=0; j < this.arr.length-1; j++) {
if (this.arr[j] < this.arr[j+1]) {
temp = this.arr[j];
this.arr[j] = this.arr[j+1];
this.arr[j+1] = temp;
try
{
Thread.sleep(5);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
repaint();
}
}
}
}
}
While developing a small task manager, I have noticed that columns aren't sorted correctly. To discard problems with my program, I have created a minimal version but it still fails to order the unique column right.
import java.awt.BorderLayout;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
public class TableSortTest extends JFrame
{
private final JTable table;
private final ATableModel model;
public TableSortTest ()
{
setDefaultCloseOperation (EXIT_ON_CLOSE);
setSize (1366, 768);
setLocationRelativeTo (null);
model = new ATableModel ();
table = new JTable ();
table.setFillsViewportHeight (true);
table.setAutoCreateRowSorter (true);
table.setModel (model);
add (new JScrollPane (table), BorderLayout.CENTER);
setVisible (true);
Worker worker = new Worker ();
worker.execute ();
}
private class Pair
{
int index;
int value;
}
private class Worker extends SwingWorker <Void, Pair>
{
#Override
protected Void doInBackground ()
{
while (!isCancelled ())
{
Random r = new Random ();
for (int i = 0; i < 100; i++)
{
int indice = getIndexInRange (0, 99);
Pair p = new Pair ();
p.index = indice;
p.value = Math.abs (r.nextInt ());
publish (p);
}
try
{
Thread.sleep (1000);
}
catch (InterruptedException ie)
{
ie.printStackTrace ();
}
}
return null;
}
#Override
public void process (List <Pair> items)
{
for (Pair p : items)
{
model.setValueAt (p.value, p.index, 0);
}
}
}
public static int getIndexInRange (int min, int max)
{
return (min + (int) (Math.random () * ((max - min) + 1)));
}
private class ATableModel extends AbstractTableModel
{
private final Integer [] data;
public ATableModel ()
{
data = new Integer [100];
Random r = new Random ();
for (int i = 0; i < 100; i++)
{
data [i] = Math.abs (r.nextInt ());
}
}
#Override
public int getColumnCount ()
{
return 1;
}
#Override
public int getRowCount ()
{
return data.length;
}
#Override
public Object getValueAt (int rowIndex, int columnIndex)
{
return data [rowIndex];
}
#Override
public void setValueAt (Object value, int rowIndex, int columnIndex)
{
data [rowIndex] = (Integer) value;
fireTableRowUpdated (rowIndex, columnIndex);
}
#Override
public Class getColumnClass (int columnIndex)
{
return Integer.class;
}
#Override
public String getColumnName (int col)
{
return "Column";
}
}
public static final void main (String [] args)
{
SwingUtilities.invokeLater (() ->
{
try
{
new TableSortTest ();
}
catch (Exception e)
{
e.printStackTrace ();
}
});
}
}
I have tried with a ScheduledExecutorService + Runnable and a Timer + TimerTask just to test if it was a threading problem, but the behavior is the same. I have also read the Java Tutorial page about the subject. Given that my table only uses standard types I think that a simple table.setAutoCreateRowSorter (true); should do the job, shouldn't it?
Shouldn't the table be sorted after every modification/addition/removal even is fired?
Thanks for your quick answer trashgod. You're right, I meant fireTableRowsUpdated () but I made a mistake when I wrote the code, sorry. The point is that fireTableRowsUpdated (rowIndex, rowIndex) and fireTableCellUpdated (rowIndex, columnIndex) both fail to sort the column correctly. In the real program most of the table rows do change from one iteration to the next so calling fireTableDataChanged () makes perfect sense. But I didn't want to use it because if I select one or more rows to send a signal to the processes or whatever the selection is lost on every update. I have explored this way and found two forms of preserving the selection but it's a bit annoying and one of them breaks the selection with the keyboard. I show the necessary additions to the original code next.
The first form saves the selection before modifying the model and restores it after every update:
...
private class Worker extends SwingWorker <Void, Pair>
{
private int [] selectedRows;
#Override
protected Void doInBackground ()
{
while (!isCancelled ())
{
// Save the selection before modifying the model
int x = table.getSelectedRowCount ();
if (x > 0)
{
selectedRows = new int [x];
int [] tableSelection = table.getSelectedRows ();
for (int i = 0; i < x; i++)
{
selectedRows [i] = table.convertRowIndexToModel (tableSelection [i]);
}
}
Random r = new Random ();
for (int i = 0; i < table.getRowCount (); i++)
{
int indice = getIndexInRange (0, table.getRowCount () - 1);
Pair p = new Pair ();
p.index = indice;
p.value = Math.abs (r.nextInt ());
publish (p);
}
// If I put the code to restore the selection here, it doesn't work...
try
{
Thread.sleep (1000);
}
catch (InterruptedException ie)
{
ie.printStackTrace ();
}
}
return null;
}
#Override
public void process (List <Pair> items)
{
for (Pair p : items)
{
model.setValueAt (p.value, p.index, 1);
}
// Restore the selection on every update
if (selectedRows != null && selectedRows.length > 0)
{
for (int i = 0; i < selectedRows.length; i++)
{
table.addRowSelectionInterval (table.convertRowIndexToView (selectedRows [i]), table.convertRowIndexToView (selectedRows [i]));
}
}
}
}
...
The second form uses a ListSelectionListener, a KeyListener, and a flag. Selection with the keyboard doesn't work. To be honest, I don't know how did I come to get this solution. It probably was by chance:
public class TableSortTestSolucionConSelectionListener extends JFrame implements KeyListener
{
...
private boolean ctrlOrShiftDown = false;
private int [] selectedRows;
#Override
public void keyPressed (KeyEvent e)
{
ctrlOrShiftDown = e.isControlDown () || e.isShiftDown ();
}
#Override
public void keyReleased (KeyEvent e)
{
ctrlOrShiftDown = e.isControlDown () || e.isShiftDown ();
}
#Override
public void keyTyped (KeyEvent e)
{
ctrlOrShiftDown = e.isControlDown () || e.isShiftDown ();
}
public TableSortTestSolucionConSelectionListener ()
{
...
ListSelectionListener lsl = new ListSelectionListener ()
{
#Override
public void valueChanged (ListSelectionEvent e)
{
if (!e.getValueIsAdjusting ())
{
if (!ctrlOrShiftDown)
{
int x = table.getSelectedRowCount ();
if (x > 0)
{
selectedRows = new int [x];
int [] tableSelection = table.getSelectedRows ();
for (int i = 0; i < x; i++)
{
selectedRows [i] = table.convertRowIndexToModel (tableSelection [i]);
}
}
}
// Disable the listener to avoid infinite recursion
table.getSelectionModel ().removeListSelectionListener (this);
if (selectedRows != null && selectedRows.length > 0)
{
for (int i = 0; i < selectedRows.length; i++)
{
table.addRowSelectionInterval (table.convertRowIndexToView (selectedRows [i]), table.convertRowIndexToView (selectedRows [i]));
}
}
table.getSelectionModel ().addListSelectionListener (this);
}
}
};
table.getSelectionModel ().addListSelectionListener (lsl);
...
}
Fortunately today I have found a simple way to get the column sorted correctly and keep the current selection. You only have to add the following to your code:
TableRowSorter trs = (TableRowSorter) table.getRowSorter ();
trs.setSortsOnUpdates (true);
With this both fireTableCellUpdated () and fireTableRowsUpdated () work as I expected. To my understanding, setAutoCreateRowSorter () is only used to sort the rows when you click on the table header.
Greetings.
Using setSortsOnUpdates(), suggested here by #trcs, is the best general solution, but you may be able to optimize updates by the choice of TableModelEvent available to subclasses of AbstractTableModel.
The critical issue is the implementation of setValueAt(). If you meant fireTableRowsUpdated(), instead of fireTableRowUpdated(), note that the parameters represent a range of rows, not a row & column. In this case, because "all cell values in the table's rows may have changed," the revised example below invokes fireTableDataChanged(). I've also changed the model to manage a List<Integer> and normalized the size, N.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
/** #see https://stackoverflow.com/a/36522182/230513 */
public class TableSortTest extends JFrame {
private final JTable table;
private final ATableModel model;
public TableSortTest() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
model = new ATableModel();
table = new JTable(model){
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 500);
}
};
table.setFillsViewportHeight(true);
table.setAutoCreateRowSorter(true);
add(new JScrollPane(table), BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
Worker worker = new Worker();
worker.execute();
}
private class Pair {
int index;
int value;
}
private class Worker extends SwingWorker<Void, Pair> {
private static final int N = 100;
private final Random r = new Random();
#Override
protected Void doInBackground() {
while (!isCancelled()) {
for (int i = 0; i < N; i++) {
int index = r.nextInt(N);
Pair p = new Pair();
p.index = index;
p.value = Math.abs(r.nextInt());
publish(p);
}
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
return null;
}
#Override
public void process(List<Pair> items) {
for (Pair p : items) {
model.setValueAt(p.value, p.index, 0);
}
}
}
private class ATableModel extends AbstractTableModel {
private static final int N = 100;
private final List<Integer> data = new ArrayList<>(N);
public ATableModel() {
final Random r = new Random();
for (int i = 0; i < N; i++) {
data.add(Math.abs(r.nextInt()));
}
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex);
}
#Override
public void setValueAt(Object value, int rowIndex, int columnIndex) {
data.set(rowIndex, (Integer) value);
fireTableDataChanged();
}
#Override
public Class getColumnClass(int columnIndex) {
return Integer.class;
}
#Override
public String getColumnName(int col) {
return "Column";
}
}
public static final void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new TableSortTest();
});
}
}
Recognizing that this is just an example, the variation below optimizes updates by publishing a List<Integer>, which is passed en bloc to the TableModel via process().
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
/**
* # see https://stackoverflow.com/a/36522182/230513
*/
public class TableSortTest extends JFrame {
private final JTable table;
private final ATableModel model;
public TableSortTest() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
model = new ATableModel();
table = new JTable(model) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 500);
}
};
table.setFillsViewportHeight(true);
table.setAutoCreateRowSorter(true);
add(new JScrollPane(table), BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
Worker worker = new Worker();
worker.execute();
}
private class Worker extends SwingWorker<List<Integer>, List<Integer>> {
private static final int N = 100;
private final Random r = new Random();
private final List<Integer> data = new ArrayList<>(N);
#Override
protected List<Integer> doInBackground() throws Exception {
while (!isCancelled()) {
data.clear();
for (int i = 0; i < N; i++) {
data.add(Math.abs(r.nextInt()));
}
publish(data);
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace(System.err);
}
}
return data;
}
#Override
protected void process(List<List<Integer>> chunks) {
for (List<Integer> chunk : chunks) {
model.update(chunk);
}
}
}
private class ATableModel extends AbstractTableModel {
private List<Integer> data = new ArrayList<>();
public void update(List<Integer> data) {
this.data = data;
fireTableDataChanged();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex);
}
#Override
public Class getColumnClass(int columnIndex) {
return Integer.class;
}
#Override
public String getColumnName(int col) {
return "Column";
}
}
public static final void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new TableSortTest();
});
}
}
Alright, I am working on a rather large program.
I will try to put in only what you need.
So right now, the comboBox is filling (with the override toString), and the first item is selected.
When I choose different parameters and force the contents of the comboBox to change, the new list is put into the comboBox and the first item is selected again.
I can see the update, so I know it is filling properly.
The problem is that when I select anything in the comboBox, nothing happens.
The first item remains selected, none of my System.out.println lines are printing, so nothing is being executed.
When I remove my override toString, everything works as intended.
The strange part, past that, is when having this override toString removed, it falls back on the parent class who has an override toString.
What is going on?
To my understanding the toString literally changes what is displayed, it does not change the data.
I am adding the objects to the comboBox, but displaying a bit of the information.
public class Belt extends Part{
//variable initialization and methods
#override
public String toString(){
String display = this.getCode() + " - " + this.color;
return display;
}
public final class Something implements ActionListener{
//variable initialization and methods
//there are several methods that call the fillBeltCombo()
GridBagConstraints c = new GridBagConstraints();
private void pad(GridBagConstraints c){
c.anchor = GridBagConstraints.NORTHWEST;
c.weightx = 1;
c.insets.left = 10;
c.insets.right = 10;
c.insets.top = 5;
c.insets.bottom = 5;
}
beltCombo = new JComboBox();
beltCombo.setVisible(true);
c.gridwidth = 2;
c.gridx = 4;
c.gridy = 9;
beltCombo.addActionListener((ActionEvent eventBelt) -> {
JComboBox beltCodeCombo1 = (JComboBox) eventBelt.getSource();
if(beltCombo.getItemCount()>0){
currentProduct.setBelt((Belt)beltCodeCombo1.getSelectedItem());
}else{/*do nothing*/}
});
pane.add(beltCombo, c);
public static void fillBeltCombo(ArrayList<Belt> list){
beltCombo.removeAllItems();
int size = list.size();
for(int x=0; x<size; x++){
beltCombo.addItem(list.get(x));
}
}
}
So taking the advice of markspace, I created a MCVE (the best I could while maintaining what my program was doing). Everything down there is done in my real program, although more complicated. I am not sure yet what my program is doing wrong, however I did find out that what I am trying to do is possible, I am able to select an item from the second comboBox while using an Override toString in the subclass. Now I just have to track the problem down in the actual code...
package minitest;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainClass {
JFrame frame;
JPanel pane;
JLabel label1, label2;
JComboBox comboA, comboB;
ArrayList<Parent> parents = new ArrayList<>();
ArrayList<Grandchild> grandkids = new ArrayList<>();
Parent parent1 = new Parent(1);
Parent parent2 = new Parent(2);
Grandchild grandkid1 = new Grandchild(1,1,"oneone");
Grandchild grandkid2 = new Grandchild(1,2,"onetwo");
Grandchild grandkid3 = new Grandchild(2,1,"twoone");
Grandchild grandkid4 = new Grandchild(2,2,"twotwo");
public class Parent{
protected int num;
public Parent(int num){
this.num = num;
}
#Override public String toString(){
return String.valueOf(num);
}
}
public class Child extends Parent{
private double dbl;
public Child(int num, double dbl){
super(num);
this.dbl = dbl;
}
}
public class Grandchild extends Child{
private String str;
public Grandchild(int num, double dbl, String str){
super(num, dbl);
this.str = str;
}
#Override public String toString(){
return this.num + " - " + this.str;
}
}
//Fill passed combo with each item from passed ArrayList
public void fillCombo(ArrayList list, JComboBox target){
target.removeAllItems();
int size = list.size();
for(int x = 0; x < size; x++){
target.addItem(list.get(x));
}
}
//remove all items from combobox except selected item
public void comboUpdate(JComboBox combo){
if(combo.getSelectedItem()==null){
/*donothing*/
}else{
int size = (combo.getItemCount()-1);
System.out.println("size: "+size);
for (int x=size; x>=0; x--) {
if(combo.getSelectedItem().equals(combo.getItemAt(x))){
/*donothing*/
System.out.println(combo.getSelectedItem()+" is selected");
}else{
System.out.println(combo.getItemAt(x)+" is now removed");
combo.removeItemAt(x);
}
}
}
}
//Add each item from passed ArrayList if not equal to the selected item
public void removeDuplicate(JComboBox combo, ArrayList array){
System.out.println("removeDuplicate: "+array);
if(combo.getSelectedItem()==null){
System.out.println("combo.getSelectedItem==null");
fillCombo(array, combo);
}else{
boolean validChoice = false;
int arraySize = array.size();
for(int x = 0; x < arraySize; x++){
if(combo.getSelectedItem().equals(array.get(x))){
System.out.println(combo.getSelectedItem()+" == "+array.get(x));
validChoice = true;
}else{
System.out.println(combo.getSelectedItem()+" != "+array.get(x));
combo.addItem(array.get(x));
}
}
if(validChoice == false){
System.out.println("removeItemAt(0)");
combo.removeItemAt(0);
}
}
}
//fill comboB with grandchild objects whos int match the selected partent objects int
public void fillComboB(){
System.out.println("grandkids: "+grandkids);
ArrayList<Grandchild> temp = copyArrayList(grandkids);
System.out.println("temp: "+temp);
comboUpdate(comboB);
int size = (temp.size()-1);
for(int x=size; x>=0; x--){
if(temp.get(x).num==((Parent)comboA.getSelectedItem()).num){
/*donothing*/
System.out.println("donothing: temp.get("+x+")="+temp.get(x));
}else{
System.out.println("temp.remove("+x+")="+temp.get(x));
temp.remove(x);
}
}
removeDuplicate(comboB, temp);
}
public ArrayList copyArrayList(ArrayList list){
ArrayList<Object> temp = new ArrayList<>();
int size = list.size();
for(int x=0; x<size; x++){
temp.add(list.get(x));
}
return temp;
}
public MainClass(){
parents.add(parent1);
parents.add(parent2);
grandkids.add(grandkid1);
grandkids.add(grandkid2);
grandkids.add(grandkid3);
grandkids.add(grandkid4);
frame = new JFrame("frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pane = new JPanel();
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
comboA = new JComboBox();
fillCombo(parents, comboA);
c.gridx = 0;
c.gridy = 0;
comboA.addActionListener((ActionEvent eventComboA) -> {
System.out.println("eventComboA");
JComboBox comboA1 = (JComboBox) eventComboA.getSource();
if(comboA.getItemCount()>0){
System.out.println("comboA.getItemCount>0: "+(Parent)comboA1.getSelectedItem());
fillComboB();
}
});
pane.add(comboA, c);
comboB = new JComboBox();
c.gridx = 0;
c.gridy = 1;
comboB.addActionListener((ActionEvent eventComboB) -> {
System.out.println("eventComboB");
JComboBox comboB1 = (JComboBox) eventComboB.getSource();
if(comboB.getItemCount()>0){
System.out.println("comboB.getItemCount>0: "+(Grandchild)comboB1.getSelectedItem());
}
});
pane.add(comboB, c);
frame.setContentPane(pane);
frame.setSize(150,150);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static void runGUI(){
JFrame.setDefaultLookAndFeelDecorated(false);
MainClass create = new MainClass();
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(() -> {
runGUI();
});
}
}
Aright, so based on yesterdays successful creation (which I was frustrated by) I continued to fill in the skeleton until it broke again.
- Adjusted classes to reflect the actual types of data that I was dealing with.
- Added my override equals method <-------problem
What happened was that when I originally created the equals override, all of the toStrings were the same, and that was displayed in the comboBox.
So the equals override looked like
#Override public boolean equals(Object other){
if (other == null) return false;
return (String.valueOf(other)).equals(this.description);
}
The problem with this, was that when I was asked to change what was displayed in the comboBox, I did not realize that the comboBox seems to use that equals check. The new other Object was
this.getCode() + " - " + this.color;
and it was being compared to num, which returned false.
Now correct me if I am wrong, but the moral of the story seems to be: JComboBox will not let you select something from the list that does not equal something on the list. So because my equals override changed how the objects were compared:
string value of object, compared to string variable of the object-
the same object was not equaling itself.
The adjusted code:
#Override public boolean equals(Object other){
if (other == null) return false;
return ((Parent)other).description.equals(this.description);
}
So I have a GUI component where a JList is dependant upon an ArrayList of tasks. This Task object has a specific index and I want the JList to reflect that specific order. This is no problem so far.
The problem is that I want the user to drag and drop the objects inside the JList to change the order. I want the ArrayList (which supports the Jlist) to also reflect those changes.
Here is my TransferHandler class :
class ListItemTransferHandler extends TransferHandler {
private final DataFlavor localObjectFlavor;
private Object[] transferedObjects = null;
public ListItemTransferHandler() {
localObjectFlavor = new ActivationDataFlavor(Object[].class, DataFlavor.javaJVMLocalObjectMimeType, "Array of items");
}
#SuppressWarnings("deprecation")
#Override
protected Transferable createTransferable(JComponent c) {
JList list = (JList) c;
indices = list.getSelectedIndices();
transferedObjects = list.getSelectedValues();
return new DataHandler(transferedObjects, localObjectFlavor.getMimeType());
}
#Override
public boolean canImport(TransferSupport info) {
if (!info.isDrop() || !info.isDataFlavorSupported(localObjectFlavor)) {
return false;
}
return true;
}
#Override
public int getSourceActions(JComponent c) {
return MOVE; // TransferHandler.COPY_OR_MOVE;
}
#SuppressWarnings("unchecked")
#Override
public boolean importData(TransferSupport info) {
if (!canImport(info)) {
return false;
}
JList target = (JList) info.getComponent();
JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
DefaultListModel listModel = (DefaultListModel) target.getModel();
int index = dl.getIndex();
int max = listModel.getSize();
if (index < 0 || index > max) {
index = max;
}
addIndex = index;
try {
Object[] values = (Object[]) info.getTransferable().getTransferData(localObjectFlavor);
addCount = values.length;
for (int i = 0; i < values.length; i++) {
int idx = index++;
listModel.add(idx, values[i]);
target.addSelectionInterval(idx, idx);
}
return true;
} catch (UnsupportedFlavorException ufe) {
ufe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
return false;
}
#Override
protected void exportDone(JComponent c, Transferable data, int action) {
cleanup(c, action == MOVE);
}
private void cleanup(JComponent c, boolean remove) {
if (remove && indices != null) {
JList source = (JList) c;
DefaultListModel model = (DefaultListModel) source.getModel();
if (addCount > 0) {
// http://java-swing-tips.googlecode.com/svn/trunk/DnDReorderList/src/java/example/MainPanel.java
for (int i = 0; i < indices.length; i++) {
if (indices[i] >= addIndex) {
indices[i] += addCount;
}
}
}
for (int i = indices.length - 1; i >= 0; i--) {
model.remove(indices[i]);
}
}
indices = null;
addCount = 0;
addIndex = -1;
}
private int[] indices = null;
private int addIndex = -1; // Location where items were added
private int addCount = 0; // Number of items added.
}
How would I implement this feature? I suppose this would help but the Task objects implements Comparable to sort.
This is related to How to get X and Y index of element inside GridLayout? post and its answers.
For whatever reason none of them suggested to extend JButton to include its position in the grid and in associated array of buttons.
I have made the following illustration that simply displays button's coordinates when it's clicked.
Extended JButton:
package buttons_array;
import javax.swing.*;
#SuppressWarnings("serial")
public class ButtonWithCoordinates extends JButton {
int coordX;
int coordY;
public ButtonWithCoordinates(String buttonText, int coordX, int coordY) {
super(buttonText);
this.coordX = coordX;
this.coordY = coordY;
}
/**
* #return the coordX
*/
public int getCoordX() {
return coordX;
}
/**
* #return the coordY
*/
public int getCoordY() {
return coordY;
}
}
sample GUI:
package buttons_array;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonsArray implements ActionListener {
private ButtonWithCoordinates buttons[][];
private int nRows;
private int nCols;
private JFrame frame;
private JPanel panel;
public ButtonsArray(int x, int y) {
if (x > 0 && y > 0) {
nRows = x;
nCols = y;
buttons = new ButtonWithCoordinates[nRows][nCols];
for (int i=0; i < nRows; ++i) {
for (int j=0; j < nCols; ++j) {
buttons[i][j] = new ButtonWithCoordinates(" ", i, j);
buttons[i][j].addActionListener(this);
}
}
} else {
throw new IllegalArgumentException("Illegal array dimensions!!!");
}
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
ButtonWithCoordinates button = (ButtonWithCoordinates) e.getSource();
button.setText(button.getCoordX() + ", " + button.getCoordY());
}
public void GUI() {
if (buttons == null) { throw new NullPointerException("Array is not initialized!!!"); }
frame = new JFrame();
panel = new JPanel();
frame.setContentPane(panel);
panel.setLayout(new GridLayout(nRows, nCols));
for (int i=0; i < nRows; ++i) {
for (int j=0; j < nCols; ++j) {
panel.add(buttons[i][j]);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ButtonsArray(3, 5).GUI();
}
});
}
}
Now my questions:
Have I been reinventing the wheel here? I mean, is there a more straightforward way to achieve the same?
Is it in any way inferior to searching through the array each time we need to find the coordinates?
The original version of this example used extension:
GridButton extends JButton
The updated version was predicated on the colloquy seen here. While extension may be appropriate in some contexts, a few alternatives are mentioned here; a client property is particularly convenient. Identifying a button from its grid coordinates is also easy:
private static final int N = 5;
List<JButton> list = new ArrayList<>();
…
private JButton getGridButton(int r, int c) {
int index = r * N + c;
return list.get(index);
}