JTable - перетаскивание
Хорошо, эта проблема из моей лиги. Я пытаюсь реализовать виджет GUI в swing, который позволяет удалять файлы на JTable и позволяет перетаскивать строки JTable для повторной сортировки. Подумайте о плейлистах VLC или о том, что в iTunes.
Я удалил файлы из ОС (Explorer, Finder и т.д.), работая отлично, но у меня есть невозможное время с повторной настройкой строк таблицы, когда файлы находятся. Проблема в том, что когда я добавляю пользовательский TransferHandler в таблицу, то перетаскивание из таблицы мгновенно убивается. Вот пример кода:
import javax.swing.*;
public class TableTest
{
public static void main (String [] argv)
{
// setup table data
String [] columns = new String [] {"Foo", "Bar", "Baz", "Quux"};
String [][] data = new String [][] {{"A", "B", "C", "D"},
{"1", "2", "3", "4"},
{"i", "ii", "iii", "iv"}};
// create table
JTable table = new JTable(data, columns);
// set up drag and drop
table.setDragEnabled(true);
table.setDropMode(DropMode.INSERT_ROWS);
table.setFillsViewportHeight(true);
TransferHandler dnd = new TransferHandler() {
// here be code to handle drops, and one would
// presume drag exporting, too
};
table.setTransferHandler(dnd);
JScrollPane scroll = new JScrollPane(table);
// create and show window
JFrame window = new JFrame();
window.getContentPane().add(scroll);
window.pack();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Запустите этот код как есть, и вы увидите, что вы не можете инициировать перетаскивание таблицы. Если вы закомментируете вызов setTransferHandler() в таблице, перетаскивание работает (т.е. когда я начинаю перетаскивать строку таблицы, Я получаю курсор X'd out circle, говорящий, что я не могу туда попасть). Но как только TransferHandler установлен для таблицы, я не могу перетаскивать любые строки. Проблема должна быть в TransferHandler, но я тщательно устранил и отладил ее, и решил, что перетаскивание никогда не запускается, если на столе есть TransferHandler. Что я делаю неправильно?
Ответы
Ответ 1
У меня была такая же проблема, она не имела никакого отношения к вашей пользовательской реализации TransferHandler. Когда вы заменяете TransferHandler, вам также необходимо удержать исходный DragSource и сообщить ему, чтобы распознать жест перетаскивания. Вам также может потребоваться реализовать свой собственный переносимый, потому что вам нужно передать его методу DragGestureEvent.startDrag().
table.setTransferHandler(new MyTransferHandler());
table.setDragEnabled(true);
DragSource source = DragSource.getDefaultDragSource();
source.createDefaultDragGestureRecognizer(table, DnDConstants.ACTION_COPY, new DragGestureListener() {
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
//grab the selected files from the table model
ArrayList<File> files = new ArrayList<File>();
for (int row : table.getSelectedRows()) {
files.add((File) dm.getValueAt(row, 1));
}
//FileTransferable is a custom Transferable implementation
Transferable transferable = new FileTransferable(files);
//and this is the magic right here
dge.startDrag(null,transferable);
}
});
Ответ 2
Не похоже, что вы правильно используете TransferHandler. Попытайтесь прочитать учебник здесь.
См. документ TransferHandler здесь. Пустой конструктор не похож на использование вне подкласса TransferHandler.
И вы не реализуете никаких функций, предоставляемых в стандартном TransferHandler, поставляемом на компонентах Swing. См. Exerpt из учебника DnD здесь (мой жирный шрифт):
Примечание. Если вы устанавливаете пользовательский TransferHandler на компонент Swing, заменяется поддержка по умолчанию. Например, если вы замените JTextField TransferHandler на тот, который обрабатывает только цвета, вы отключите его способность поддерживать импорт и экспорт текста. Если вы должны заменить установленный по умолчанию TransferHandler - например, тот, который обрабатывает текст - вам нужно будет повторно реализовать возможности импорта и экспорта текста. Это не должно быть настолько обширным, как то, что предлагает Swing, - это может быть так же просто, как поддержка вкуса данных StringFlavor, в зависимости от потребностей вашего приложения.
Ответ 3
Я думаю, проблема в том, что пустой TransferHandler фактически предотвращает появление событий DnD. Здесь есть пример, который может иметь значение.
http://www.java2s.com/Code/Java/Swing-JFC/ExtendedDnDDragandDropDemo.htm
Ответ 4
Я не хотел вникать в орехи и болты того, что происходило, поэтому я просто делегировал методы, которые мне не интересовали старый TransferHandler.
tree.setDragEnabled(true);
tree.setDropMode(DropMode.XXXX);
tree.setTransferHandler(new MyTransferHandler(tree.getTransferHandler());
Начните стандартную настройку, но передайте старый TransferHandler в свой собственный TransferHandler.
private class MyTransferHandler extends TransferHandler {
private TransferHandler delegate;
public MyTransferHandler(TransferHandler delegate) {
this.delegate = delegate;
}
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
return delegate.canImport(comp, transferFlavors);
}
public boolean canImport(TransferSupport support) {
return true;
}
protected Transferable createTransferable(JComponent c) {
try {
Method method = delegate.getClass().getDeclaredMethod("createTransferable", JComponent.class);
method.setAccessible(true);
return (Transferable) method.invoke(delegate, c);
} catch (Exception e) {
return super.createTransferable(c);
}
}
public void exportAsDrag(JComponent comp, InputEvent event, int action) {
delegate.exportAsDrag(comp, event, action);
}
protected void exportDone(JComponent source, Transferable data, int action) {
try {
Method method = delegate.getClass().getDeclaredMethod("exportDone", JComponent.class, Transferable.class,
int.class);
method.setAccessible(true);
method.invoke(delegate, source, data, action);
} catch (Exception e) {
super.exportDone(source, data, action);
}
}
public int getSourceActions(JComponent c) {
return delegate.getSourceActions(c);
}
public Icon getVisualRepresentation(Transferable t) {
return delegate.getVisualRepresentation(t);
}
public boolean importData(JComponent comp, Transferable t) {
return delegate.importData(comp, t);
}
public boolean importData(TransferHandler.TransferSupport support) {
return delegate.importData(support);
}
}
Один из способов заключается в том, что методы createTransferable (JComponent) и exportDone (JComponent, Transferable, int) защищены, поэтому вам нужно сделать отражение, чтобы делегировать эти методы. Когда я не сделал этого делегата с отражением, стратегия не сработала. Как только я сделал эту делегацию, перетаскивание работало, как ожидалось, без изменения DragSource или для записи нового Transferable.