im working on a small application that fetches information from a DB and displays it in a ListView , then I select elements of the list and move it to a second list, I must be able to add and remove elements between list. I have also implemented both lists with FilteredLists for search functionality. My code works, Im able to switch elements between lists, but I get an OutOfBounds exception when I try to return elements from the second list to the first one, or when I move the last element of the 1st list to the second one. Can anyone help me to figure it out?
Thanks in advance.
The exception
Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
at com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
at javafx.collections.transformation.FilteredList.get(FilteredList.java:172)
at javafx.scene.control.ListCell.updateItem(ListCell.java:459)
at javafx.scene.control.ListCell.lambda$new$160(ListCell.java:167)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.transformation.FilteredList.sourceChanged(FilteredList.java:147)
at javafx.collections.transformation.TransformationList.lambda$getListener$23(TransformationList.java:106)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at com.sun.javafx.collections.ObservableListWrapper.removeAll(ObservableListWrapper.java:185)
at com.kalypso.WCExporter.MainController.removeItemFromList(MainController.java:351)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8411)
at javafx.scene.control.Button.fire(Button.java:185)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$353(GlassViewEventHandler.java:432)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
Lists Declaration
@FXML
private JFXListView leftList;
@FXML
private JFXTextField leftFilter;
@FXML
private JFXListView rightList;
@FXML
private JFXTextField rightFilter;
private ObservableList<String> leftData = FXCollections.observableArrayList();
private ObservableList<String> rightData = FXCollections.observableArrayList();
private FilteredList<String> leftFilteredData;
private FilteredList<String> rightFilteredData;
This method populates the first list
This list holds all the values that are going to be used
private void loadResultList(){
if( session != null && session.isOpen()) {
//Clear list View
leftList.getItems().clear();
//Get entity node
HibernateUtil hibernateUtil = new HibernateUtil();
//Get query results
List resultSet = hibernateUtil.executeSQLQuery(session, selectedEntity.getValue().get("query").asText());
//Wrap resultset into Observable list
resultSet.forEach(result -> leftData.add(((Map)result).get("NAME").toString()));
// 1. Wrap the ObservableList in a FilteredList (initially display all data).
leftFilteredData = new FilteredList<>(leftData, p -> true);
// 2. Set the filter Predicate whenever the filter changes.
wrapListAndAddFiltering(leftFilter, leftFilteredData);
leftList.setItems(leftFilteredData);
leftList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
} else{
Alert noDBCon = new Alert(Alert.AlertType.ERROR);
noDBCon.setTitle("No Database connection");
noDBCon.setHeaderText("No Database Connection");
noDBCon.setContentText("Select and connect to a DB before doing any transaction.");
noDBCon.showAndWait();
}
}
private void wrapListAndAddFiltering(JFXTextField filterField,FilteredList<String> filteredData) {
filterField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(item -> {
String filter = filterField.getText().toLowerCase();
if (newValue == null || newValue.isEmpty()) {
return true;
}
if (item.toLowerCase().contains(filter)) {
return true;
}
return false;
});
});
}
Methods to add and remove elements
@FXML
private void addItemToList(){
//1.- Add elements to right list
//Wrap left elements into Observable list
leftList.getSelectionModel().getSelectedItems().forEach(item -> rightData.add(item.toString()));
//Wrap the ObservableList in a FilteredList (initially display all data).
rightFilteredData = new FilteredList<>(rightData, p -> true);
wrapListAndAddFiltering(rightFilter, rightFilteredData);
rightList.setItems(rightFilteredData);
//2.- Remove elements from left list
leftData.removeAll(leftList.getSelectionModel().getSelectedItems());
leftFilteredData = new FilteredList<>(leftData, p -> true);
wrapListAndAddFiltering( leftFilter, leftFilteredData );
leftList.setItems(leftFilteredData);
}
@FXML
private void removeItemFromList(){
//1.-add elements to left list
rightList.getSelectionModel().getSelectedItems().forEach(item -> leftData.add(item.toString()));
leftFilteredData = new FilteredList<>(leftData, p -> true);
wrapListAndAddFiltering(leftFilter, leftFilteredData);
leftList.setItems(leftFilteredData);
//1.-Remove items from right list
ObservableList<String> temp = FXCollections.observableArrayList();
rightList.getSelectionModel().getSelectedItems().forEach(el -> temp.add(el.toString()));
//rightList.getSelectionModel().getSelectedItems().forEach(rightData::remove);
rightData.removeAll(temp);
//rightData.removeAll();
rightFilteredData = new FilteredList<>(rightData, p -> true);
wrapListAndAddFiltering( rightFilter, rightFilteredData );
rightList.setItems(rightFilteredData);
}
The GUI
I created an MCVE. Comments in the code. It shows how to set up the FilteredList
and how to move data from one ListView
to another.
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* @author blj0011
*/
public class JavaFXApplication239 extends Application
{
@Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
/**
*
* @author blj0011
*/
public class FXMLDocumentController implements Initializable
{
@FXML
private ListView lvLeft, lvRight;
@FXML
private TextField tfLeft, tfRight;
ObservableList<String> leftData = FXCollections.observableArrayList();
ObservableList<String> rightData = FXCollections.observableArrayList();
FilteredList<String> filteredLeftData, filteredRightData;
@Override
public void initialize(URL url, ResourceBundle rb)
{
leftData.addAll(getFakeDataFromDB());//get data from DB
//rightData.addAll(getFakeDataFromDB());//Used for testing
filteredLeftData = new FilteredList(leftData, s -> true);
filteredRightData = new FilteredList(rightData, s -> true);
//Set filtered Lists
tfLeft.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null || newValue.length() == 0) {
filteredLeftData.setPredicate(null);
}
else {
filteredLeftData.setPredicate(t -> {
return t.toUpperCase().startsWith(newValue.toUpperCase());
});
}
});
tfRight.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null || newValue.length() == 0) {
filteredRightData.setPredicate(null);
}
else {
filteredRightData.setPredicate(t -> {
return t.toUpperCase().startsWith(newValue.toUpperCase());
});
}
});
//Set listview items
lvLeft.setItems(filteredLeftData);
lvRight.setItems(filteredRightData);
//Set selecton model selection mode
lvLeft.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
lvRight.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
}
@FXML
private void handleBtnActionMoveToLeft(ActionEvent actionEvent)
{
List<String> itemsToMove = new ArrayList(lvRight.getSelectionModel().getSelectedItems());//If you don't do this "new ArrayList(..):", then you need to first addAll then removeAll
System.out.println("list size: " + itemsToMove.size());
rightData.removeAll(itemsToMove);
leftData.addAll(itemsToMove);
}
@FXML
private void handleBtnActionMoveToRight(ActionEvent actionEvent)
{
List<String> itemsToMove = new ArrayList(lvLeft.getSelectionModel().getSelectedItems());//If you don't do this "new ArrayList(..):", then you need to first addAll then removeAll
System.out.println("list size: " + itemsToMove.size());
leftData.removeAll(itemsToMove);
rightData.addAll(itemsToMove);
}
List<String> getFakeDataFromDB()
{
List<String> tempList = new ArrayList();
tempList.add("Hello");
tempList.add("Hello World!");
tempList.add("Bye");
tempList.add("Bye World!");
tempList.add("Been");
tempList.add("Bad");
return tempList;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication239.FXMLDocumentController">
<children>
<ListView fx:id="lvLeft" prefHeight="200.0" prefWidth="200.0" />
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" HBox.hgrow="ALWAYS">
<children>
<Label text="Left" />
<TextField fx:id="tfLeft" />
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="5.0">
<children>
<Button mnemonicParsing="false" onAction="#handleBtnActionMoveToRight" text="Move To Right" />
<Button mnemonicParsing="false" onAction="#handleBtnActionMoveToLeft" text="Move To Left" />
</children>
<opaqueInsets>
<Insets />
</opaqueInsets>
</VBox>
<Label text="Right" />
<TextField fx:id="tfRight" />
</children>
</VBox>
<ListView fx:id="lvRight" prefHeight="200.0" prefWidth="200.0" />
</children>
</HBox>