Сортировать JTable, когда мышь отпущена
У меня есть JTable со свойством AutoCreateRowSorter, установленным в true. Он отлично работает при нажатии, но если я перемещаю мышь во время щелчка только на 1 пиксель, сортировка не появляется, что неудобно в некоторых ситуациях. Как это исправить?
UPD Я просто хочу, чтобы сортировка появлялась при отпускании мыши.
1 ответ
Решение
Грязный (читай: не надо, если вы не совсем отчаялись и точно знаете, что делаете!) Подход - подключиться к MouseListener, установленному uiDelegate, и переместить триггер сортировки в выпущенный метод. Это включает в себя
- настраиваемый MouseListener, который делегирует все события, за исключением тех, которые были нажаты, для первоначально установленного и сортирует перед передачей выпущенного
- заменить оригинал на кастом
- обновлять замену каждый раз, когда изменяется LAF (потому что оригинал контролируется пользовательским интерфейсом). Это требует создания подкласса JTableHeader и сделать подключение в updateUI
Пользовательский слушатель:
public static class EventHook implements MouseListener {
private JTableHeader header;
private MouseListener delegate;
public EventHook(JTableHeader header) {
this.header = header;
installHook();
}
protected void installHook() {
MouseListener[] listeners = header.getMouseListeners();
for (int i = 0; i < listeners.length; i++) {
MouseListener l = listeners[i];
if (l.getClass().getName().contains("TableHeaderUI")) {
this.delegate = l;
listeners[i] = this;
}
header.removeMouseListener(l);
}
for (MouseListener l : listeners) {
header.addMouseListener(l);
}
}
public void uninstallHook() {
MouseListener[] listeners = header.getMouseListeners();
for (int i = 0; i < listeners.length; i++) {
MouseListener l = listeners[i];
if (l == this) {
listeners[i] = delegate;
}
header.removeMouseListener(l);
}
for (MouseListener l : listeners) {
header.addMouseListener(l);
}
}
@Override
public void mouseReleased(MouseEvent e) {
// sort before calling super
sort(e);
delegate.mouseReleased(e);
}
private void sort(MouseEvent e) {
if (!header.isEnabled()) {
return;
}
// do nothing if dragged
if (header.getDraggedDistance() != 0) {
return;
}
if (e.getClickCount() % 2 == 1 &&
SwingUtilities.isLeftMouseButton(e)) {
JTable table = header.getTable();
RowSorter sorter;
if (table != null && (sorter = table.getRowSorter()) != null) {
int columnIndex = header.columnAtPoint(e.getPoint());
if (columnIndex != -1) {
columnIndex = table.convertColumnIndexToModel(
columnIndex);
sorter.toggleSortOrder(columnIndex);
}
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
// swallow the clicked - want to trigger sort on released
}
@Override
public void mousePressed(MouseEvent e) {
delegate.mousePressed(e);
}
@Override
public void mouseEntered(MouseEvent e) {
delegate.mouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
delegate.mouseExited(e);
}
}
Подкласс JTableHeader
public static class MyTableHeader extends JTableHeader {
private EventHook hook;
public MyTableHeader(TableColumnModel model) {
super(model);
}
@Override
public void updateUI() {
if (hook != null) {
hook.uninstallHook();
hook = null;
}
super.updateUI();
hook = new EventHook(this);
}
}
Использование, либо подкласс JTable и переопределить createDefaultTableHeader или вручную установить заголовок:
// either subclass
JTable table = new JTable(new AncientSwingTeam()) {
@Override
protected JTableHeader createDefaultTableHeader() {
return new MyTableHeader(getColumnModel());
}
};
table.setAutoCreateRowSorter(true);