Потерянный список JTable, потерянный при обновлении строки
У меня есть JTable, отображающий данные, которые поддерживаются застекленным списком. Выбор таблицы теряется при очень определенных условиях. Выбранная строка должна быть обновлена, чтобы отсортированный столбец был изменен, чтобы выбранная строка переместилась на новую позицию.
Я считаю, что столкнулся с этой ошибкой GlazedList, но я не могу найти обходной путь для нее: https://java.net/jira/browse/GLAZEDLISTS-194
Я создал некоторый исходный код, который демонстрирует проблему:
public class SelectionLost {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
/*
* Create and set up the table.
*/
final JTable table = new JTable();
final EventList<DisplayElement> data = new BasicEventList<>();
ObservableElementList<DisplayElement> observeData =
new ObservableElementList<>(data, new DisplayConnector());
final SortedList<DisplayElement> sortedData =
new SortedList<>(observeData);
sortedData.setMode(SortedList.STRICT_SORT_ORDER);
//populate our list with some data.
data.add(new DisplayElement("a", "a"));
data.add(new DisplayElement("b", "b"));
data.add(new DisplayElement("c", "c"));
data.add(new DisplayElement("d", "d"));
//Set up the table models.
AdvancedTableModel<DisplayElement> model
= GlazedListsSwing.eventTableModelWithThreadProxyList(sortedData, new DisplayTableFormat());
table.setModel(model);
final AdvancedListSelectionModel<DisplayElement> select
= GlazedListsSwing.eventSelectionModelWithThreadProxyList(sortedData);
table.setSelectionModel(select);
TableComparatorChooser<DisplayElement> tc = TableComparatorChooser.install(
table, sortedData, TableComparatorChooser.MULTIPLE_COLUMN_MOUSE_WITH_UNDO);
//sort on the first column
tc.appendComparator(0, 0, false);
//select the first row
table.getSelectionModel().setSelectionInterval(0, 0);
/*
* Create UI elements
*/
JFrame frame = new JFrame();
JPanel panel = new JPanel(new BorderLayout());
frame.add(panel);
JScrollPane scroll = new JScrollPane(table);
panel.add(scroll);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Random r = new Random();
/*
* Create a timer which will change the elements data. When the selected
* element's data is changed so that it moves into a new index position,
* the selection is lost.
*/
TimerTask tt = new TimerTask() {
@Override
public void run() {
data.getReadWriteLock().writeLock().lock();
try{
int row = r.nextInt(data.size());
int col = r.nextInt(2);
Character c = (char)(r.nextInt(26)+'a');
DisplayElement d = data.get(row);
if(col == 0)
d.setFirst(c.toString());
else
d.setSecond(c.toString());
}
finally{
data.getReadWriteLock().writeLock().unlock();
}
}
};
java.util.Timer t = new java.util.Timer();
t.schedule(tt, 250, 250);
}
});
}
/*
* These are the data elements stored in the table
*/
public static class DisplayElement implements Comparable<DisplayElement>{
private String first, second;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public DisplayElement(String first, String second) {
this.first = first;
this.second = second;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
pcs.firePropertyChange("first", null, null);
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
pcs.firePropertyChange("second", null, null);
}
@Override
public int compareTo(DisplayElement o) {
int comp = first.compareTo(o.first);
if(comp != 0)
return comp;
return second.compareTo(o.second);
}
public void addPropertyChangeListener(PropertyChangeListener l){
pcs.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l ){
pcs.removePropertyChangeListener(l);
}
}
/*
* Table format for glazed lists
*/
public static class DisplayTableFormat implements AdvancedTableFormat<DisplayElement>, WritableTableFormat<DisplayElement>{
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(int i) {
if(i == 0 )
return "first";
else
return "second";
}
@Override
public Object getColumnValue(DisplayElement e, int i) {
if(i == 0)
return e.first;
else
return e.second;
}
@Override
public Class getColumnClass(int i) {
return String.class;
}
@Override
public Comparator getColumnComparator(int i) {
return GlazedLists.comparableComparator();
}
@Override
public boolean isEditable(DisplayElement e, int i) {
return true;
}
@Override
public DisplayElement setColumnValue(DisplayElement e, Object o, int i) {
if(i == 0)
e.first = (String) o;
else
e.second = (String)o;
return e;
}
};
/*
* Connector for observable lists.
*/
public static class DisplayConnector implements ObservableElementList.Connector<DisplayElement>{
ObservableElementList<? extends DisplayElement> list;
PropertyChangeListener myListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
list.elementChanged(evt.getSource());
}
};
@Override
public EventListener installListener(DisplayElement e) {
e.addPropertyChangeListener(myListener);
return myListener;
}
@Override
public void uninstallListener(DisplayElement e, EventListener el) {
e.removePropertyChangeListener(myListener);
}
@Override
public void setObservableElementList(ObservableElementList<? extends DisplayElement> oel) {
list = oel;
}
}
}
1 ответ
Попробовав многочисленные исправления, я наконец нашел то, что решило проблему. Я создал слушателя, который восстановил выбор. Единственным недостатком этого является то, что пользователь больше не может "отменить выбор" строки, нажав CTRL+ щелкнув по ней. Если кто-нибудь может придумать лучшее решение, я буду рад его увидеть. Я нахожу это немного хакерским.
/*
* This listener fixes the problem where we would lose selection.
* Note that it prevents the user from "unselecting" a row.
*/
public static class GlazedListBug194Listener <T> implements ListSelectionListener {
private T lastSelectedElement;
private final JTable table;
private final AdvancedTableModel<T> model;
private final AdvancedListSelectionModel<T> select;
public GlazedListBug194Listener(JTable table,
AdvancedTableModel<T> model,
AdvancedListSelectionModel<T> select) {
this.table = table;
this.model = model;
this.select = select;
}
@Override
public void valueChanged(ListSelectionEvent e) {
int selectedRow = table.convertRowIndexToModel(table.getSelectedRow());
if(selectedRow < 0){
if(table.getRowCount() == 0){
//table was cleared
lastSelectedElement = null;
}
else{
//restore selection
for(int i = 0; i < table.getRowCount(); i++){
if(model.getElementAt(i) == lastSelectedElement){
select.setSelectionInterval(i, i);
break;
}
}
}
}
else{
lastSelectedElement = model.getElementAt(selectedRow);
}
}
}