You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX ListView拖拽报错:IndexOutOfBoundsException索引-1越界

JavaFX ListView拖拽时抛出IndexOutOfBoundsException的解决方法

你的代码可以正常填充ListView,但拖拽元素时触发IndexOutOfBoundsException(索引-1越界),核心问题出在拖拽数据传递不匹配、索引获取逻辑错误以及空判断写法不当,以下是具体修复方案:

问题根源分析

  1. 拖拽数据格式不匹配:在setOnDragDetected中你将对象转为字符串存入Dragboard,但在setOnDragDropped中却检查自定义格式CATEGORY_DATA_FORMAT,导致无法正确获取拖拽对象,items.indexOf(droppedCategory)返回-1。
  2. 重复元素的索引错误:列表中存在重复的Categoria实例,使用items.indexOf(getItem())会返回第一个匹配元素的索引,而非当前cell对应的真实索引。
  3. 空判断写法错误:if (getItem().equals(null))会在getItem()为null时抛出NullPointerException,正确写法应为if (getItem() == null)。

修复后的完整代码

修正CategoryOrder类中的CategoriaCell内部类

private class CategoriaCell extends ListCell<Categoria> {
    // 定义自定义数据格式,用于传递Categoria对象
    private static final DataFormat CATEGORY_DATA_FORMAT = new DataFormat("application/x-categoria");

    public CategoriaCell() {
        setContentDisplay(ContentDisplay.TEXT_ONLY);
        setAlignment(Pos.CENTER_LEFT);

        setOnDragDetected(event -> {
            if (getItem() == null) {
                return;
            }
            Dragboard dragboard = startDragAndDrop(TransferMode.MOVE);
            ClipboardContent content = new ClipboardContent();
            // 直接传递Categoria对象,而非字符串
            content.put(CATEGORY_DATA_FORMAT, getItem());
            dragboard.setContent(content);
            event.consume();
        });

        setOnDragOver(event -> {
            // 检查自定义数据格式,而非字符串
            if (event.getGestureSource() != this &&
                    event.getDragboard().hasContent(CATEGORY_DATA_FORMAT)) {
                event.acceptTransferModes(TransferMode.MOVE);
            }
            event.consume();
        });

        setOnDragEntered(event -> {
            if (event.getGestureSource() != this &&
                    event.getDragboard().hasContent(CATEGORY_DATA_FORMAT)) {
                setOpacity(0.3);
            }
        });

        setOnDragExited(event -> {
            if (event.getGestureSource() != this &&
                    event.getDragboard().hasContent(CATEGORY_DATA_FORMAT)) {
                setOpacity(1);
            }
        });

        setOnDragDropped(event -> {
            // 修正空判断逻辑
            if (getItem() == null) {
                event.setDropCompleted(false);
                event.consume();
                return;
            }

            Dragboard db = event.getDragboard();
            boolean success = false;

            if (db.hasContent(CATEGORY_DATA_FORMAT)) {
                Categoria droppedCategory = (Categoria) db.getContent(CATEGORY_DATA_FORMAT);
                ObservableList<Categoria> items = getListView().getItems();

                // 获取拖拽元素的索引,以及当前cell的真实索引(用getIndex()而非indexOf)
                int draggedIdx = items.indexOf(droppedCategory);
                int thisIdx = getIndex();

                // 确保索引有效,避免越界
                if (draggedIdx != -1 && thisIdx != -1) {
                    // 使用Collections.swap简化交换逻辑
                    Collections.swap(items, draggedIdx, thisIdx);
                    success = true;
                }
            }
            event.setDropCompleted(success);
            event.consume();
        });
        setOnDragDone(DragEvent::consume);
    }

    @Override
    protected void updateItem(Categoria item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
        } else {
            setText(item.getNome());
        }
    }
}

关键修复点总结

  • 使用自定义DataFormat传递对象:避免字符串转换带来的类型问题,确保拖拽数据能正确还原为Categoria实例。
  • 用getIndex()获取当前cell索引:直接获取cell对应的列表索引,解决重复元素导致的indexOf返回错误值的问题。
  • 修正空对象判断:将getItem().equals(null)改为getItem() == null,防止空指针异常。
  • 增加索引有效性检查:在交换元素前判断draggedIdx和thisIdx是否为-1,避免触发越界异常。

内容的提问来源于stack exchange,提问作者TrySoHard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 19:53:19