Как я могу заморозить выделение строки JTable внутри панели прокрутки
У меня есть JTable, который часто обновляется. Он находится внутри панели прокрутки.
Иногда мы видим сообщение, в которое мы хотели бы углубиться. К сожалению, из-за количества обновлений сообщение будет прокручиваться с видимого экрана, прежде чем мы сможем закончить просмотр.
Я хотел бы иметь возможность заморозить область просмотра / прокрутку, пока мы проверяем сообщение, но все же позволяю модели таблицы обновляться новыми сообщениями.
Я могу получить выбранную строку, чтобы перейти наверх с этим кодом:
int firstSelectedRow = table.getSelectedRow();
Rectangle rowLocation = table.getCellRect(firstSelectedRow, 0, false);
scroll.getVerticalScrollBar().setValue(rowLocation.y);
freezeScrolling.setText("Resume Updates");
но это происходит только при нажатии кнопки, а затем быстро исчезает.
Есть ли способ указать панели просмотра / прокрутки заморозить выделенную область и отключить ее, чтобы панель прокрутки обновлялась так, как вы ожидаете?
2 ответа
Хорошо, этот сильно меня дразнил, поэтому я потратил некоторое время, чтобы найти способ для этого. Я нашел два варианта:
- Тот, который я предпочитаю, потому что я нахожу его намного проще: заблокировать новые записи в табличной модели и сохранить их в буфере. Всякий раз, когда мы размораживаемся, мы сбрасываем буфер в модель таблицы.
- Второй вариант заключается в поиске текущей верхней видимой строки. Если пользователь прокручивает, мы фиксируем верхнюю видимую строку. По мере обновления модели мы находим захваченную строку в таблице и устанавливаем для нее позицию порта просмотра.
Вот SSCCE, который иллюстрирует оба механизма (не обращайте внимания на тщательность разработки кода, я протестировал кучу вещей - расширение JScrollPane на самом деле не очень хорошая идея, все это можно сделать внешне).
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class Tables {
private static class JScrollPaneExtension extends JScrollPane implements TableModelListener, AdjustmentListener {
private boolean frozen = false;
private Vector<String> rowData;
private final JTable table;
private final MyTableModel tableModel;
private JScrollPaneExtension(JTable table, MyTableModel tableModel) {
super(table);
this.table = table;
this.tableModel = tableModel;
tableModel.addTableModelListener(this);
getVerticalScrollBar().addAdjustmentListener(this);
}
public boolean isFrozen() {
return frozen;
}
public void setFrozen(boolean frozen) {
if (frozen != this.frozen) {
this.frozen = frozen;
if (frozen) {
captureCurrentTopRowData();
} else {
rowData = null;
}
}
}
private void captureCurrentTopRowData() {
int row = table.rowAtPoint(getViewport().getViewPosition());
if (row > -1) {
rowData = (Vector<String>) tableModel.getDataVector().get(row);
}
}
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
if (frozen) {
captureCurrentTopRowData();
scrollToRowData();
}
}
@Override
public void tableChanged(TableModelEvent e) {
scrollToRowData();
}
private void scrollToRowData() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (frozen) {
int index = tableModel.getDataVector().indexOf(rowData);
getViewport().setViewPosition(table.getCellRect(index, 0, true).getLocation());
}
}
});
}
}
public static class MyTableModel extends DefaultTableModel {
private int count;
private boolean frozen = false;
private List<Vector<String>> buffer = new ArrayList<Vector<String>>();
public MyTableModel() {
addColumn("Test");
}
public void insertNewRow() {
Vector<String> rowData = createNewRowData();
if (isFrozen()) {
buffer.add(rowData);
} else {
insertRow(0, rowData);
}
}
private Vector<String> createNewRowData() {
Vector<String> data = new Vector<String>(1);
data.add("Hello-" + (count++));
return data;
}
public boolean isFrozen() {
return frozen;
}
public void setFrozen(boolean frozen) {
if (frozen == this.frozen) {
return;
}
this.frozen = frozen;
if (!frozen) {
flushBuffer();
}
}
private void flushBuffer() {
for (Vector<String> rowData : buffer) {
insertRow(0, rowData);
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final MyTableModel model = new MyTableModel();
JTable table = new JTable(model);
final JScrollPaneExtension scroll = new JScrollPaneExtension(table, model);
JPanel panel = new JPanel();
final JButton freeze = new JButton("Freeze");
freeze.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (model.isFrozen()) {
freeze.setText("Freeze model");
} else {
freeze.setText("Continue");
}
model.setFrozen(!model.isFrozen());
}
});
final JButton freeze2 = new JButton("Freeze scroll");
freeze2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (scroll.isFrozen()) {
freeze2.setText("Freeze scroll");
} else {
freeze2.setText("Resume scroll");
}
scroll.setFrozen(!scroll.isFrozen());
}
});
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
frame.add(scroll);
panel.add(freeze);
panel.add(freeze2);
frame.getContentPane().add(panel, BorderLayout.NORTH);
frame.pack();
frame.setVisible(true);
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
model.insertNewRow();
}
});
}
}, new Date(), 300);
}
}
Возможно, вы сможете адаптировать подход, показанный здесь. Это условия прокрутки на основе значения getValueIsAdjusting()
в AdjustmentListener
, Нажмите на большой палец, чтобы приостановить прокрутку; отпустить, чтобы возобновить.