У меня есть список со многими элементами внутри панели прокрутки, и я реализовал перетаскивание в списке. Когда я выбираю элемент из списка и перетаскиваю его в конец списка, список должен автоматически прокручиваться вниз, пока я держу мышь близко к краю. Это нормально работает в Windows, но в Linux список прокручивается на один элемент, а затем останавливается.
Вот короткая программа, которая выявляет эту ошибку:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.WindowConstants;
public class JListAutoscroll {
protected static Container createUI() {
JList<String> jlist = new JList<>(generateData(100));
setDragAndDrop(jlist);
JScrollPane scrollPane = new JScrollPane(jlist);
JPanel panel = new JPanel(new BorderLayout());
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
private static void setDragAndDrop(JList<String> jlist) {
jlist.setDragEnabled(true);
jlist.setDropMode(DropMode.INSERT);
jlist.setTransferHandler(new ListTransferHandler());
}
private static String[] generateData(int nRows) {
String rows[] = new String[nRows];
for (int i = 0; i < rows.length; i++) {
rows[i] = "element " + i;
}
return rows;
}
private static class ListTransferHandler extends TransferHandler {
@Override
public int getSourceActions(JComponent component) {
return COPY_OR_MOVE;
}
@Override
protected Transferable createTransferable(JComponent component) {
return new ListItemTransferable((JList)component);
}
@Override
public boolean canImport(TransferHandler.TransferSupport support) {
return true;
}
@Override
public boolean importData(TransferHandler.TransferSupport support) {
return true;
}
}
private static class ListItemTransferable implements Transferable {
private String item;
public ListItemTransferable(JList<String> jlist) {
item = jlist.getSelectedValue();
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { DataFlavor.stringFlavor };
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(DataFlavor.stringFlavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if(!isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return item;
}
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("JList Autoscroll");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setContentPane(createUI());
frame.setPreferredSize(new Dimension(400, 600));
frame.pack();
frame.setVisible(true);
}
});
}
}
Я реализовал простой TransferHandler, который ничего не делает при перетаскивании, но его достаточно, чтобы показать проблему при перетаскивании к краю списка.
Кажется, это известная ошибка в JDK, которая лучше всего описана в этом отчете. Я видел некоторые предлагаемые обходные пути, такие как этот или этот, но мне непонятно, как я могу их реализовать. Мне кажется, что мне нужно создать подкласс DropTarget, и компонент, который я использую с ним, должен реализовывать интерфейс Autoscroll
. Но JList
этого не реализует! Кроме того, если я установлю DropTarget в списке вместо TransferHandler, не потеряю ли я все поведение перетаскивания по умолчанию, реализованное TransferHandler?
Итак, как я могу изменить свою программу, чтобы обойти эту ошибку?