JavaFX ListView拖拽报错:IndexOutOfBoundsException索引-1越界
JavaFX ListView拖拽时抛出IndexOutOfBoundsException的解决方法
你的代码可以正常填充ListView,但拖拽元素时触发IndexOutOfBoundsException(索引-1越界),核心问题出在拖拽数据传递不匹配、索引获取逻辑错误以及空判断写法不当,以下是具体修复方案:
问题根源分析
- 拖拽数据格式不匹配:在
setOnDragDetected中你将对象转为字符串存入Dragboard,但在setOnDragDropped中却检查自定义格式CATEGORY_DATA_FORMAT,导致无法正确获取拖拽对象,items.indexOf(droppedCategory)返回-1。 - 重复元素的索引错误:列表中存在重复的
Categoria实例,使用items.indexOf(getItem())会返回第一个匹配元素的索引,而非当前cell对应的真实索引。 - 空判断写法错误:
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
相关产品推荐
相关产品推荐

