I have a JTable with a custom JTableModel, now I want to create a custom comparator, so the user can order the all column with my custom method.
I have this code:
ColumnListener
public class ColumnListener extends MouseAdapter {
protected JTable table;
public ColumnListener(JTable t) {
table = t;
}
public void mouseClicked(MouseEvent e) {
TableColumnModel colModel = table.getColumnModel();
int columnModelIndex = colModel.getColumnIndexAtX(e.getX());
int modelIndex = colModel.getColumn(columnModelIndex)
.getModelIndex();
if (modelIndex < 0)
return;
if (sortCol == modelIndex)
isSortAsc = !isSortAsc;
else
sortCol = modelIndex;
for (int i = 0; i < columnsCount; i++) {
TableColumn column = colModel.getColumn(i);
column.setHeaderValue(getColumnName(column.getModelIndex()));
}
table.getTableHeader().repaint();
Collections.sort(v,new MyComparator(isSortAsc,columnModelIndex));
table.tableChanged(new TableModelEvent(MyTableModelSpese.this));
table.repaint();
}
}
MyComparator
class MyComparator implements Comparator {
protected boolean isSortAsc;
protected Integer numeroColonna;
public MyComparator( boolean sortAsc,Integer numberColumn) {
isSortAsc = sortAsc;
numeroColonna = numberColumn;
}
public int compare(Object o1, Object o2) {
if (!(o1 instanceof NotaSpese) || !(o2 instanceof NotaSpese))
return 0;
NotaSpese s1 = (NotaSpese) o1;
NotaSpese s2 = (NotaSpese) o2;
int result = 0;
if(numeroColonna==1){
//ordinamento per data
result = s1.getDataOperazioneData().compareTo(s2.getDataOperazioneData());
}
if (!isSortAsc)
result = -result;
return result;
}
public boolean equals(Object obj) {
if (obj instanceof MyComparator) {
MyComparator compObj = (MyComparator) obj;
return compObj.isSortAsc == isSortAsc;
}
return false;
}
}
When I try to order a column, Data (column ==1) and I see the collection after the order
Collections.sort(v,new MyComparator(isSortAsc,columnModelIndex));
the order of the object in collection v is ok.
But if I see the element in the table are not ordered by my method.
I see this data
2015-01-31
2015-12-30
2014-12-28
How can I fixed it?
I think you can avoid using a custom MouseAdapter if you use a TableRowSorter
TableRowSorter tableRowSorter = new TableRowSorter(tableModel.getModel());
//add your comparator to every column you want. in this case column with index 1
tableRowSorter.setComparator(1, new MyComparator(false));
table.setRowSorter(tableRowSorter);
You could also parametarize your Comparator, so you don't have to cast to NotaSpese
private class MyComparator implements Comparator<NotaSpese> {
protected boolean isSortAsc;
public MyComparator(boolean sortAsc) {
isSortAsc = sortAsc;
}
#Override
public int compare(NotaSpese o1, NotaSpese o2) {
int result = o1.getDataOperazioneData().compareTo(o2.getDataOperazioneData());
if (!isSortAsc) {
result = -result;
}
return result;
}
}
Related
I have a class SimpleHistogram<DT> that takes in a generic array DT[] and I'm supposed to set the count, the number of times the element occurs in the array, of a specific item in the array to int count.
Here is what I have so far:
public class SimpleHistogram<DT> implements Histogram<DT>, Iterable<DT> {
DT[] items;
int size;
public SimpleHistogram() {
}
public SimpleHistogram(DT[] items) {
this.items = items;
}
#Override
public void setCount(DT item, int count) {
int n = 0;
Iterator<DT> L = this.iterator();
while (L.hasNext()) {
DT dt = L.next();
if (dt == item) { // if dt equals to item, meaning item IS present, then
n+=count; // set the count of the item to count
} else
{this.add(dt, count)} // if its not equal, meaning its not there, then add the item and the count of the item
}
}
private class Iterate implements Iterator<DT> {
int index = 0;
boolean lastRemoved = false;
#Override
public boolean hasNext() {
return (index < items.length-1);
}
#Override
public DT next() {
if (index < (items.length) -1)
throw new NoSuchElementException("No element at index");
DT object = items[index];
index++;
lastRemoved = false;
return object;
}
}
I'm struggling to implement the function setCount( DT item, int count) which is supposed to set the count of item to count.
Aditionally, if item does not exist already in the list, then we are supposed to add the item in and then set the count of the item to count.
I have provided explanations for what I intended to do but due to the fact that I am new to this, I haven't found sources that can properly clear this doubt, so any help would be greatly appreciated
Edit Here is the full code in case you may want to derive something from it. Test cases also presented below.
package histogram;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
// TODO: Uncomment this and make sure to implement all the methods
public class SimpleHistogram<DT> implements Histogram<DT>, Iterable<DT> {
DT[] items;
int size;
public SimpleHistogram() {
}
public SimpleHistogram(DT[] items) {
this.items = items;
}
#Override
public void setCount(DT item, int count) {
int n = 0;
Iterator<DT> L = this.iterator();
while (L.hasNext()) {
DT dt = L.next();
if (dt == item) { // if dt equals to item, meaning item IS present, then
n+=count; // set the count of the item to count
} else
{this.add(dt, count)} // if its not equal, meaning its not there, then add the item and the count of the item
}
}
private class Iterate implements Iterator<DT> {
int index = 0;
boolean lastRemoved = false;
#Override
public boolean hasNext() {
return (index < items.length-1);
}
#Override
public DT next() {
if (index < (items.length) -1)
throw new NoSuchElementException("No element at index");
DT object = items[index];
index++;
lastRemoved = false;
return object;
}
}
public int getCount(DT item) {
int n = 0;
Iterator<DT> L = this.iterator();
while (L.hasNext()) {
DT dt = L.next();
if (dt == item) {
n++;
}
}
return n;
}
#Override
public Iterator<DT> iterator() {
return new Iterate();
}
#Override
public int getTotalCount() {
return items.length;
}
}
Test cases:
public class SimpleHistogramTest {
#Test
public void testHistogram() {
Character[] target = {'a','b','c','a'};
Histogram<Character> h = new SimpleHistogram<>(target);
Iterator<Character> iter = h.iterator();
int elemCount = 0;
while(iter.hasNext()) {
iter.next();
elemCount++;
}
assertEquals(3, elemCount);
assertEquals(2, h.getCount('a'));
assertEquals(1, h.getCount('b'));
assertEquals(1, h.getCount('c'));
assertEquals(4, h.getTotalCount());
}
}
Would something like this satisfy the requirements? It's essentially a wrapper around a map, where the elements are the keys, and each value is a count of that element. Presumably, the Histogram interface has additional operations like getCount(T); here, you'd just get the count from the encapsulated map.
class SimpleHistogram<T> implements Histogram<T>, Iterable<T> {
private final Map<T, Integer> bins = new HashMap<>();
SimpleHistogram() {
this(List.of());
}
SimpleHistogram(List<? extends T> items) {
for (T item : items) {
Integer count = bins.getOrDefault(item, 0);
bins.put(item, count + 1);
}
}
#Override
public void setCount(T item, int count) {
bins.put(item, count);
}
#Override
public Iterator<T> iterator() {
return bins.keySet().iterator();
}
}
== compares the reference of an object in Java. equals() is also same.
If you want to compare two objects(dt and item) wit the same value, you need to override hashCode() and equals().
After that, you use equals() rather than ==.
Here is reference link.
Compare two objects with .equals() and == operator
I need to create a vector class that implements the Map interface, but in the put method it returns NullException, I need help implementing the put method, I can't find this type of content on the internet
public class Vetor_map implements Map<Key, Student> {
private int nElements;
private Map<Key, Student> mapa[];
public Vetor_map(int max) {
mapa = new Map[max];
nElements = 0;
}
// PUT
#Override
public Estudante put(Key key, Estudants value) {
if (!isFull()) {
mapa[nElements].put(key, value);
nElements++;
return value;
}
return value;
}
public boolean isEmpty() {
// TODO Auto-generated method stub
if (nElements == 0)
return true;
return false;
}
public boolean isFull() {
if (nElements == mapa.length) {
return true;
}
return false;
}
Class main:
public static void main(String[] args) {
// TODO Auto-generated method stub
Key ch = new Key();
Student es = new Student();
Vetor_map vm = new Vetor_map(10);
System.out.println("Key: " + ch + ", Estudant: "+ es);
vm.put(ch, es);
Error
A Map is a collection of entries where each entry is a key-value pair. So your Vetor_map class should implement java.util.Map – which it does – but I think that you should also create a class that implements interface Map.Entry.
In the below code, I added an inner class VectorMapEntry that implements Map.Entry and therefore class VectorMap – which implements interface Map – contains an array of VectorMapEntry instances. From there it is just a simple matter of implementing the methods of both interfaces, namely Map and Map.Entry.
Note the following in the below code.
I changed the name from Vetor_map to VectorMap.
I added methods toString() and main() to class VectorMap for testing purposes only. I also added method toString() to classes Key and Student also for testing purposes.
You did not supply code for classes Key and Student so I added skeleton definitions for those two classes. Note that each class – Key and Student – must each implement method equals. I only added method hashCode since the javadoc for method equals() recommends doing so.
VectorMap cannot contain a null Key but it can contain a null Student as a value for a given Key.
Each Key in VectorMap is unique. When calling method put() with a Key that already exists in VectorMap, the existing value is replaced.
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class VectorMap implements Map<Key, Student> {
private static class VectorMapEntry implements Map.Entry<Key, Student> {
private Key key;
private Student student;
public VectorMapEntry(Key k, Student s) {
key = k;
student = s;
}
public Key getKey() {
return key;
}
public Student getStudent() {
return student;
}
public void setStudent(Student s) {
student = s;
}
#Override
public Student getValue() {
return student;
}
#Override
public Student setValue(Student value) {
student = value;
return value;
}
}
private int nElements;
private VectorMapEntry[] entries;
public VectorMap(int initialSize) {
if (initialSize <= 0) {
throw new IllegalArgumentException("Initial size must be positive.");
}
entries = new VectorMapEntry[initialSize];
}
#Override
public int size() {
return nElements;
}
#Override
public boolean isEmpty() {
return nElements == 0;
}
#Override
public boolean containsKey(Object key) {
boolean found = false;
for (VectorMapEntry entry : entries) {
if (entry == null) {
break;
}
found = entry.getKey().equals(key);
if (found) {
break;
}
}
return found;
}
#Override
public boolean containsValue(Object value) {
boolean found = false;
for (VectorMapEntry entry : entries) {
if (entry == null) {
break;
}
found = entry.getStudent().equals(value);
if (found) {
break;
}
}
return found;
}
#Override
public Student get(Object key) {
for (VectorMapEntry entry : entries) {
if (entry == null) {
break;
}
if (entry.getKey().equals(key)) {
return entry.getStudent();
}
}
return null;
}
#Override
public Student put(Key key, Student value) {
if (key == null) {
throw new IllegalArgumentException("Null key.");
}
boolean found = false;
for (VectorMapEntry entry : entries) {
if (entry == null) {
break;
}
if (entry.getKey().equals(key)) {
entry.setStudent(value);
found = true;
break;
}
}
if (!found) {
if (nElements == entries.length) {
throw new RuntimeException("Map is full.");
}
entries[nElements] = new VectorMapEntry(key, value);
nElements++;
}
return value;
}
#Override
public Student remove(Object key) {
int index = 0;
Student s = null;
for (VectorMapEntry entry : entries) {
if (entry == null) {
break;
}
if (entry.getKey().equals(key)) {
s = entry.getStudent();
break;
}
index++;
}
if (index < nElements) {
for (int i = index; i < nElements - 1; i++) {
entries[i] = entries[i + 1];
}
nElements--;
}
return s;
}
#Override
public void putAll(Map<? extends Key, ? extends Student> m) {
if (m != null) {
m.entrySet()
.stream()
.forEach(ent -> put(ent.getKey(), ent.getValue()));
}
}
#Override
public void clear() {
for (int i = 0; i < nElements; i++) {
entries[i] = null;
}
nElements = 0;
}
#Override
public Set<Key> keySet() {
Set<Key> keySet = new HashSet<>();
for (int i = 0; i < nElements; i++) {
keySet.add(entries[i].getKey());
}
return keySet;
}
#Override
public Collection<Student> values() {
Collection<Student> vals = new ArrayList<>(nElements);
for (int i = 0; i < nElements; i++) {
vals.add(entries[i].getStudent());
}
return vals;
}
#Override
public Set<Map.Entry<Key, Student>> entrySet() {
Set<Map.Entry<Key, Student>> entSet = new HashSet<>();
for (int i = 0; i < nElements; i++) {
entSet.add(entries[i]);
}
return entSet;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
boolean first = true;
for (int i = 0; i < nElements; i++) {
if (first) {
first = false;
}
else {
sb.append(", ");
}
sb.append(entries[i].getKey());
sb.append('=');
sb.append(entries[i].getValue());
}
sb.append(']');
return sb.toString();
}
public static void main(String[] args) {
Key ch = new Key();
Student es = new Student();
VectorMap vm = new VectorMap(10);
System.out.println("Key: " + ch + ", Estudant: "+ es);
vm.put(ch, es);
System.out.println(vm);
}
}
class Key {
public boolean equals(Object obj) {
return false;
}
public int hashCode() {
return -1;
}
public String toString() {
return "Key";
}
}
class Student {
public boolean equals(Object obj) {
return false;
}
public int hashCode() {
return -1;
}
public String toString() {
return "Student";
}
}
Also note that it would be possible to change the above code such that VectorMap could (theoretically) contain an infinite number of key-value pairs. This could be achieved by simply enlarging the entries array whenever it gets completely filled.
I want to sort a LinkedHashMap based on object attribute and using Comparable. Here is my code:
public class MapClass{
public static void main(String args[]){
sortMapBasedOnValueObjectUsingComprable();
}
public static void sortMapBasedOnValueObjectUsingComprable(){
Map map = new LinkedHashMap();
map.put("2",new Pojo("456"));
map.put("4",new Pojo("366"));
map.put("1",new Pojo("466"));
map.put("8",new Pojo("5666"));
map.put("9",new Pojo("456"));
map.put("3",new Pojo("66"));
// How to sort ...?
Set<Map.Entry<String,Object>> st = map.entrySet();
Iterator itr = st.iterator();
while(itr.hasNext()){
Map.Entry mxt= (Map.Entry)itr.next();
Pojo pj = (Pojo)mxt.getValue();
System.out.println(pj.getX());
}
}
public class Pojo implements Serializable, Comparable<Object>{
private static final long serialVersionUID = 1L;
private String x;
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public Pojo(String x) {
super();
this.x = x;
}
#Override
public int compareTo(Object o) {
return 0;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((x == null) ? 0 : x.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pojo other = (Pojo) obj;
if (x == null) {
if (other.x != null)
return false;
} else if (!x.equals(other.x))
return false;
return true;
}
}
So u need to first dump your entrySet into a List and then sort it.You can't sort a set unless you are using a TreeSet.I will prefer to dump it into a List which implements RandomAccess so that I can fetch any element by its index.
Map<String,Object> map = new LinkedHashMap<>();
Set<Map.Entry<String,Object>> st = map.entrySet();
List<Map.Entry<String,Object>> listSort = new ArrayList<>(st);
Collections.sort(listSort,new Comparator<Map.Entry<String,Object>(){
public int compare(Map.Entry<String,Object> entry1,Map.Entry<String,Object> entry2){
return ((Comparable)entry1.getValue).compareTo(entry2.getValue.compareTo);
});
Just iterate the list and you can get the elements sorted
Note:- I have implemented a Comparator using anonymous inner class and this Comparator in turn uses the Comparable instance of Pojo class
coding in Java Eclipse here. Making a booking system. The idea is to take the info from the database ,store it in the ArrayList and from the ArrayList show it in the GUI through JTable. Having some problems with the last part and just can't figure it out..
ArrayList:
import java.util.ArrayList;
public class CarList
{
private ArrayList<Car> cars;
public CarList()
{
cars = new ArrayList<Car>();
}
public int getNumberOfCars()
{
return cars.size();
}
public Car getCar(String CarMake)
{
for (int i = 0; i < cars.size(); i++)
{
if (cars.get(i).getMake() == CarMake)
{
return cars.get(i);
}
}
return null;
}
public int size()
{
return cars.size();
}
public void add(Car car)
{
if (!this.ModelExists(car.getModel()))
{
cars.add(car);
}
}
public Boolean ModelExists(String Model)
{
for (Car c : cars)
{
if (c.getModel().equals(Model))
{
return true;
}
}
return false;
}
public void remove(String CarMake)
{
for (int i = 0; i < cars.size(); i++)
{
if (cars.get(i).getMake() == CarMake)
{
cars.remove(i);
}
}
}
public String toString()
{
String returnStr = "";
for (int i = 0; i < cars.size(); i++)
{
Car temp = cars.get(i);
returnStr += temp + "\n";
}
return returnStr;
}
}
Adapter to get the data from the db to the arraylist:
public CarList getAllCars()
{
MyDatabase myDB = new MyDatabase();
CarList cars = new CarList();
try
{
myDB.openMySQLDatabase("db", "root", "");
String sql = "SELECT Make, Model, LicenseNumber, Color, Year," +
"HorsePower, TimeUntilService, ConsumptionPerKm," +
"NumberOfSeats, NumberOfDoors, Transmission, ClimateControl,Price "
+ "FROM cars";
System.out.println(sql);
Object[][] result = myDB.returnSQLQueryResult(sql);
for (int rows = 0; rows < result.length; rows++)
{
System.out.println("result row");
String make = (String) result[rows][0];
String model = (String) result[rows][1];
String licenseNumber = (String) result[rows][2];
String color = (String) result[rows][3];
int year = (int) result[rows][4];
String horsePower = (String) result[rows][5];
String timeUntilService = (String) result[rows][6];
String consumptionPerKm = (String) result[rows][7];
int numberOfSeats = (int) result[rows][8];
int numberOfDoors = (int) result[rows][9];
String transmission = (String) result[rows][10];
String climateControl = (String) result[rows][11];
int price = (int) result[rows][12];
cars.add(new Car(make, model, licenseNumber, color, year, horsePower,
timeUntilService, consumptionPerKm, climateControl, numberOfSeats, numberOfDoors, transmission, climateControl, price));
}
}
catch (SQLException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
finally
{
try
{
myDB.closeDatabase();
}
catch (SQLException e)
{
e.printStackTrace();
}
}
System.out.println(cars.size());
return cars;
}
JTable:
panelBottomRight = new JPanel();
panelBottomRight.setLayout(new BorderLayout());
panelBottomRight.setBorder(new TitledBorder(BorderFactory
.createLineBorder(Color.black), "[Cars]", 2, 0));
tableBottomRightCenter = new JPanel();
tableBottomRightCenter.setLayout(new BorderLayout());
String[] columnNames = { "Make", "Model", "LicenseNumber", "Color",
"Year", "HorsePower", "TimeUntilService",
"ConsumptionPerKm", "NumberOfSeats", "NumberOfDoors",
"ClimateControl" };
CarList cars= new CarList();
String[][] data = {};
// Create table with database data
tableBottomR = new JTable(data, columnNames);
tableBottomR.setAutoCreateRowSorter(true);
tableBottomR.getTableHeader().setReorderingAllowed(false);
tableBottomR.setModel(new DefaultTableModel(data, columnNames)
{
#Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
});
tableBottomRightCenter.add(tableBottomR, BorderLayout.CENTER);
scrollPane2 = new JScrollPane(tableBottomR);
scrollPane2
.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
tableBottomRightCenter.add(scrollPane2);
panelBottomRight.add(tableBottomRightCenter, BorderLayout.CENTER);
There are a few things that jump out.
In you CarList, the getCar method is comparing object references instead of comparing the contents of the String
For String comparison, you should be using String#equals, for example...
public Car getCar(String CarMake) {
for (int i = 0; i < cars.size(); i++) {
//if (cars.get(i).getMake() == CarMake) {
if (cars.get(i).getMake().equals(CarMake)) {
return cars.get(i);
}
}
return null;
}
You don't seem to be using the getAllCars method to populate the table model, but are simply creating a series of empty table models.
Personally, I'm not a fan of DefaultTableModel, especially given the fact that you have a Car object and CarList object, i would require you to undo all this work to use it, instead, I prefer to create my own, specialised, implementation, which allows me to provide greater control, for example...
public class CarModel extends AbstractTableModel {
private String[] columnNames = { "Make", "Model", "LicenseNumber", "Color",
"Year", "HorsePower", "TimeUntilService",
"ConsumptionPerKm", "NumberOfSeats", "NumberOfDoors",
"ClimateControl" };
private CarList carList;
public CarModel(CarList list) {
carList = list;
}
public CarList getCarList() {
return carList;
}
#Override
public int getRowCount() {
return getCarList().getNumberOfCars();
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
#Override
public Class<?> getColumnClass(int columnIndex) {
Class type = String.class;
switch (columnIndex) {
case 0:
type = String.class;
break;
// ...etc...
}
return type;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Car car = getCarList().getCarAt(rowIndex);
Object value = null;
switch (columnIndex) {
case 0:
value = car.getMake();
break;
//...etc...
}
return value;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
}
This, obviously, will require you to add a getCarAt(int) method to your CarList to return the Car at the given index.
Then, you simply need to extract the data from the database and apply the resulting CarList to the table model, for example...
CarList carList = getAllCars();
CarTableModel model = new CarTableModel(carList);
Then, you just need to add it to your UI, for example...
JTable table = new JTable(model);
add(new JScrollPane(table));
Take a look at How to use tables for more details and examples...
I want my JComboBox to group multiple options together, similar to the HTML optgroup:
<select>
<optgroup label="A">
<option/>
<option/>
</optgroup>
</select>
I could not find any solution for this in Swing. Manipulating the UI-Renderer for the Combobox seems to be a bad idea, as it's OS & L&F-dependent (and they are private so cannot extend).
Consider the following implementation as a basic guide how to apply custom styling and create non-selectable items:
public class ExtendedComboBox extends JComboBox {
public ExtendedComboBox() {
setModel(new ExtendedComboBoxModel());
setRenderer(new ExtendedListCellRenderer());
}
public void addDelimiter(String text) {
this.addItem(new Delimiter(text));
}
private static class ExtendedComboBoxModel extends DefaultComboBoxModel {
#Override
public void setSelectedItem(Object anObject) {
if (!(anObject instanceof Delimiter)) {
super.setSelectedItem(anObject);
} else {
int index = getIndexOf(anObject);
if (index < getSize()) {
setSelectedItem(getElementAt(index+1));
}
}
}
}
private static class ExtendedListCellRenderer
extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
if (!(value instanceof Delimiter)) {
return super.getListCellRendererComponent(list, value, index,
isSelected, cellHasFocus);
} else {
JLabel label = new JLabel(value.toString());
Font f = label.getFont();
label.setFont(f.deriveFont(f.getStyle()
| Font.BOLD | Font.ITALIC));
return label;
}
}
}
private static class Delimiter {
private String text;
private Delimiter(String text) {
this.text = text;
}
#Override
public String toString() {
return text.toString();
}
}
}
You can do this in a custom renderer, as discussed in How to Use Combo Boxes: Providing a Custom Renderer.
I don't believe that there is one simple way of doing this, but there is a way to do it.
I would implement a data model class that indicates the grouping that you've describe above. Place instances of those data models in your javax.swing.ComboBoxModel implementation instance.
You can then implement a javax.swing.ListCellRenderer to format the output as you like with indents for the text data. You may just want to extend the javax.swing.DefaultListCellRenderer or possibly borrow its implementation wholesale from the Java source.
As for the L&F you should be able to stay within normal guidelines by using the above methods and you won't have to fight with figuring out how to implement it. Look at the default Swing components they will provide a lot of insight in to how to deal with L&F.
Additionally, I think there are mechanisms (you'll have to forgive me, it's been YEARS since I've done full Swing development) to allow you to determine if an item is selectable or not.
I was wanting this myself today, and I've spent the day figuring it out piecing things together to implement a similar model with a JList rather than with the suggested JComboBox. I've finally come up with a solution using GlazedLists EventList and SeparatorList with the corresponding DefaultEventListModel. I override the CellRenderer and the DefaultListSelectionModel. In the end I posted my own answer to my own question on this: How to prevent selection of SeparatorList.Separator in a JList?
Here's my final working code:
public class MyFrame extends javax.swing.JFrame {
private final EventList<BibleVersion> bibleVersions;
private final SeparatorList<BibleVersion> versionsByLang;
private boolean[] enabledFlags;
public MyFrame(){
bibleVersions = new BasicEventList<>();
bibleVersions.add(new BibleVersion("CEI2008", "Testo della Conferenza Episcopale Italiana", "2008", "Italian"));
bibleVersions.add(new BibleVersion("LUZZI", "Diodati Nuova Riveduta - Luzzi", "1927", "Italian"));
bibleVersions.add(new BibleVersion("NVBSE", "Nova Vulgata - Bibliorum Sacrorum Editio", "1979", "Latin"));
bibleVersions.add(new BibleVersion("NABRE", "New American Bible - Revised Edition", "2011", "English"));
bibleVersions.add(new BibleVersion("KJV", "King James Version", "1611", "English"));
versionsByLang = new SeparatorList<>(bibleVersions, new VersionComparator(),1, 1000);
int listLength = versionsByLang.size();
enabledFlags = new boolean[listLength];
ListIterator itr = versionsByLang.listIterator();
while(itr.hasNext()){
enabledFlags[itr.nextIndex()] = !(itr.next().getClass().getSimpleName().equals("GroupSeparator"));
}
jList = new javax.swing.JList();
jList.setModel(new DefaultEventListModel<>(versionsByLang));
jList.setCellRenderer(new VersionCellRenderer());
jList.setSelectionModel(new DisabledItemSelectionModel());
ListSelectionModel listSelectionModel = jList.getSelectionModel();
listSelectionModel.addListSelectionListener(new SharedListSelectionHandler());
}
public static class BibleVersion {
private String abbrev;
private String fullname;
private String year;
private String lang;
public BibleVersion(String abbrev, String fullname, String year, String lang) {
this.abbrev = abbrev;
this.fullname = fullname;
this.year = year;
this.lang = lang;
}
public String getAbbrev() {
return abbrev;
}
public void setAbbrev(String abbrev) {
this.abbrev = abbrev;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public String getLang() {
return lang;
}
public void setLang(String lang) {
this.lang = lang;
}
#Override
public String toString() {
return this.getAbbrev() + " — " + this.getFullname() + " (" + this.getYear() + ")"; //To change body of generated methods, choose Tools | Templates.
}
}
private static class VersionComparator implements Comparator<BibleVersion> {
#Override
public int compare(BibleVersion o1, BibleVersion o2) {
return o1.getLang().compareTo(o2.getLang());
}
}
private static class VersionCellRenderer extends DefaultListCellRenderer{
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof SeparatorList.Separator) {
SeparatorList.Separator separator = (SeparatorList.Separator) value;
BibleVersion bibleversion = (BibleVersion)separator.getGroup().get(0);
String lbl = "-- " + bibleversion.getLang() + " --";
label.setText(lbl);
label.setFont(label.getFont().deriveFont(Font.BOLD));
label.setBackground(Color.decode("#004400"));
label.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
label.setEnabled(false);
} else {
label.setFont(label.getFont().deriveFont(Font.PLAIN));
label.setBorder(BorderFactory.createEmptyBorder(0,15,0,0));
}
return label;
}
}
private class DisabledItemSelectionModel extends DefaultListSelectionModel {
private static final long serialVersionUID = 1L;
#Override
public void setSelectionInterval(int index0, int index1) {
if(index0 < index1){
for (int i = index0; i <= index1; i++){
if(enabledFlags[i]){
super.addSelectionInterval(i, i);
}
}
}
else if(index1 < index0){
for (int i = index1; i <= index0; i++){
if(enabledFlags[i]){
super.addSelectionInterval(i, i);
}
}
}
else if(index0 == index1){
if(enabledFlags[index0]){ super.setSelectionInterval(index0,index0); }
}
}
#Override
public void addSelectionInterval(int index0, int index1) {
if(index0 < index1){
for (int i = index0; i <= index1; i++){
if(enabledFlags[i]){
super.addSelectionInterval(i, i);
}
}
}
else if(index1 < index0){
for (int i = index1; i <= index0; i++){
if(enabledFlags[i]){
super.addSelectionInterval(i, i);
}
}
}
else if(index0 == index1){
if(enabledFlags[index0]){ super.addSelectionInterval(index0,index0); }
}
}
}
private class SharedListSelectionHandler implements ListSelectionListener {
#Override
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel lsm = (ListSelectionModel)e.getSource();
StringBuilder output = new StringBuilder();
int firstIndex = e.getFirstIndex();
int lastIndex = e.getLastIndex();
boolean isAdjusting = e.getValueIsAdjusting();
output.append("Event for indexes ");
output.append(firstIndex);
output.append(" - ");
output.append(lastIndex);
output.append("; isAdjusting is ");
output.append(isAdjusting);
output.append("; selected indexes:");
if (lsm.isSelectionEmpty()) {
output.append(" <none>");
} else {
// Find out which indexes are selected.
int minIndex = lsm.getMinSelectionIndex();
int maxIndex = lsm.getMaxSelectionIndex();
for (int i = minIndex; i <= maxIndex; i++) {
if (lsm.isSelectedIndex(i)) {
output.append(" ");
output.append(i);
}
}
}
output.append(System.getProperty("line.separator"));
System.out.println(output.toString());
}
}
}