当前位置: 首页 > 知识库问答 >
问题:

如何用鼠标拖动在JavaFX ListView中选择多行

柳仲卿
2023-03-14

我是JavaFX的新手,我似乎找不到如何做到这一点。

我在Vbox中有一个ListView,我用一个可见的字符串列表填充它。我已经将ListView的SelectionMode设置为MULTIPLE,这允许我在按住Ctrl或Shift键的同时选择多个项目。

我希望能够单击一行并向下拖动鼠标并选择多行,但我不知道该怎么做。我已经尝试了几次搜索,似乎只找到了拖放,这不是我需要的。

@FXML private ListView availableColumnList;

private ObservableList<String> availableColumns = FXCollections.<String>observableArrayList("One","Two","Three","Four");

availableColumnList.getItems().addAll(availableColumns);

availableColumnList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

共有1个答案

范霄
2023-03-14

如果您使用的是JavaFX 10,那么您可以扩展<code>ListViewSkin</code>并在其中添加功能。您需要JavaFX 10或更高版本的原因是,这是因为VirtualContainerBase类添加了getVirtualFlow()方法的时候,该类扩展了ListViewSkinAnimationTimer,通过VirtualFlow#scrollPixels(double)方法滚动 >。

下面是一个概念验证。当鼠标靠近ListView的顶部(或左侧)或底部(或右侧)时,它会自动滚动ListView。当鼠标进入单元格时,项目被选中(粗略地)。如果您想取消选择项目,如果您开始以相反的方向拖动鼠标,那么您需要自己实现。您可能想要实现的另一件事是,如果ListView被隐藏或从场景中删除,则停止动画计时器

注意:下面使用“完全按下拖动释放”手势。换句话说,存在MouseEvent处理程序和MouseDragEvent处理程序的混合。使用MouseDragEvents的原因是,它们可以传递到其他节点,而不仅仅是原始节点(与“简单的按下-拖动-释放”手势不同)。有关详细信息,请参阅本文档。

主类

import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.stage.Stage;

public final class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        var listView = IntStream.range(0, 1000)
                .mapToObj(Integer::toString)
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(FXCollections::observableArrayList),
                        ListView::new
                ));
        // Sets the custom skin. Can also be set via CSS.
        listView.setSkin(new CustomListViewSkin<>(listView));
        listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        primaryStage.setScene(new Scene(listView, 600, 400));
        primaryStage.show();
    }

}

CustomListViewSkin.java

import javafx.animation.AnimationTimer;
import javafx.geometry.Rectangle2D;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.skin.ListViewSkin;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;

public class CustomListViewSkin<T> extends ListViewSkin<T> {

    private static final double DISTANCE = 10;
    private static final double PERCENTAGE = 0.05;

    private AnimationTimer scrollAnimation = new AnimationTimer() {

        @Override
        public void handle(long now) {
            if (direction == -1) {
                getVirtualFlow().scrollPixels(-DISTANCE);
            } else if (direction == 1) {
                getVirtualFlow().scrollPixels(DISTANCE);
            }
        }

    };

    private Rectangle2D leftUpArea;
    private Rectangle2D rightDownArea;

    private int direction = 0;
    private int anchorIndex = -1;

    public CustomListViewSkin(final ListView<T> control) {
        super(control);
        final var flow = getVirtualFlow();
        final var factory = flow.getCellFactory();

        // decorate the actual cell factory
        flow.setCellFactory(vf -> {
            final var cell = factory.call(flow);

            // handle drag start
            cell.addEventHandler(MouseEvent.DRAG_DETECTED, event -> {
                if (control.getSelectionModel().getSelectionMode() == SelectionMode.MULTIPLE) {
                    event.consume();
                    cell.startFullDrag();
                    anchorIndex = cell.getIndex();
                }
            });

            // handle selecting items when the mouse-drag enters the cell
            cell.addEventHandler(MouseDragEvent.MOUSE_DRAG_ENTERED, event -> {
                event.consume();
                if (event.getGestureSource() != cell) {
                    final var model = control.getSelectionModel();
                    if (anchorIndex < cell.getIndex()) {
                        model.selectRange(anchorIndex, cell.getIndex() + 1);
                    } else {
                        model.selectRange(cell.getIndex(), anchorIndex + 1);
                    }
                }
            });

            return cell;
        });

        // handle the auto-scroll functionality
        flow.addEventHandler(MouseDragEvent.MOUSE_DRAG_OVER, event -> {
            event.consume();
            if (leftUpArea.contains(event.getX(), event.getY())) {
                direction = -1;
                scrollAnimation.start();
            } else if (rightDownArea.contains(event.getX(), event.getY())) {
                direction = 1;
                scrollAnimation.start();
            } else {
                direction = 0;
                scrollAnimation.stop();
            }
        });

        // stop the animation when the mouse exits the flow/list (desired?)
        flow.addEventHandler(MouseDragEvent.MOUSE_DRAG_EXITED, event -> {
            event.consume();
            scrollAnimation.stop();
        });

        // handle stopping the animation and reset the state when the mouse
        // is released. Added to VirtualFlow because it doesn't matter
        // which cell receives the event.
        flow.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> {
            if (anchorIndex != -1) {
                event.consume();
                anchorIndex = -1;
                scrollAnimation.stop();
            }
        });

        updateAutoScrollAreas();
        registerChangeListener(control.orientationProperty(), obs -> updateAutoScrollAreas());
        registerChangeListener(flow.widthProperty(), obs -> updateAutoScrollAreas());
        registerChangeListener(flow.heightProperty(), obs -> updateAutoScrollAreas());
    }

    // computes the regions where the mouse needs to be
    // in order to start auto-scrolling. The regions depend
    // on the orientation of the ListView.
    private void updateAutoScrollAreas() {
        final var flow = getVirtualFlow();
        switch (getSkinnable().getOrientation()) {
            case HORIZONTAL:
                final double width = flow.getWidth() * PERCENTAGE;
                leftUpArea = new Rectangle2D(0, 0, width, flow.getHeight());
                rightDownArea = new Rectangle2D(flow.getWidth() - width, 0, width, flow.getHeight());
                break;
            case VERTICAL:
                final double height = flow.getHeight() * PERCENTAGE;
                leftUpArea = new Rectangle2D(0, 0, flow.getWidth(), height);
                rightDownArea = new Rectangle2D(0, flow.getHeight() - height, flow.getWidth(), height);
                break;
            default:
                throw new AssertionError();
        }
    }

    @Override
    public void dispose() {
        unregisterChangeListeners(getSkinnable().orientationProperty());
        unregisterChangeListeners(getVirtualFlow().widthProperty());
        unregisterChangeListeners(getVirtualFlow().heightProperty());
        super.dispose();

        scrollAnimation.stop();
        scrollAnimation = null;
    }
}

注意:正如kleopatra所提到的,至少有些功能更适合行为类。然而,为了简单起见,我决定只使用现有的公共皮肤类(通过扩展它)。同样,以上只是概念的证明。

 类似资料:
  • 在ListView中,我可以按下< kbd>Ctrl Shift并单击项目进行选择。但是,我想拖动鼠标来选择项目(如DataGridView)。我尝试了下面的代码,我遇到了这样的问题: 我的代码:

  • 问题内容: 因此,正如标题所述,我想在鼠标拖动时移动椭圆。我先声明了椭圆并将其画出(因为我有8个椭圆,它们带有不同的颜色信息。四个是白色,另一个是红色)。我尝试做我对矩形所做的事情: 但这似乎不起作用。它给我一个错误 我有点困惑,因为我已经阅读了文档,并且Ellipse2D.Double存在这样的变量。 这是一个MCVE: 那么是否有替代算法,或者我只是在语法上缺少什么?我想知道解决方案。谢谢。

  • 本文向大家介绍javascript实现淡蓝色的鼠标拖动选择框实例,包括了javascript实现淡蓝色的鼠标拖动选择框实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了javascript实现淡蓝色的鼠标拖动选择框。分享给大家供大家参考。具体实现方法如下: 希望本文所述对大家的javascript程序设计有所帮助。

  • 问题内容: 我想知道如何在基于X,Y鼠标坐标的android上执行拖动吗? 考虑两个简单的示例,Team Viewer / QuickSupport 分别 在远程智能手机和Windows Paint Pen上绘制“密码模式” 。 我所能做的就是模拟触摸(使用dispatchGesture()和也AccessibilityNodeInfo.ACTION_CLICK)。 我找到了这些相关链接,但不知道

  • 本文向大家介绍js实现鼠标拖拽多选功能示例,包括了js实现鼠标拖拽多选功能示例的使用技巧和注意事项,需要的朋友参考一下 最近做了一个用js实现鼠标拖拽多选的功能,于是整理了一下思路,写了一个小demo: 遮罩出现: 被遮罩盖住的,即为选中的块(背景色为粉色) 下面是具体代码,注释已在文中,与大家交流。 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • 问题内容: 我在JLabel中有一个图像。 我想要以下功能。 -我单击JLabel内部的位置(在图像上)。 -按下鼠标按钮,即可更改JLabel中图像的位置。(我将图片拖到JLabel中的不同位置) 好吧,这意味着在许多情况下,图片将被裁剪并且在视野之外。 请告诉我如何实现此功能? 什么是要添加到我的JLabel的正确事件监听器? 问题答案: 这是一个基本的例子… 通过将标签划分为3x3网格来工作