Issue
I'm new to java/javaFX so be patient with me.
The problem I'm having is that I don't get auto scrolling to work.
I've tried these solutions but didn't get them to work:
href="https://stackoverflow.com/questions/14779135/javafx-tableview-auto-scroll-to-the-last">javafx tableview auto scroll to the last
JavaFX how to add ChangeListener to auto scroll TableView
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.Image;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage stage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("DogRegister.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setMinHeight(900);
stage.setMinWidth(1200);
stage.setResizable(false);
stage.setTitle("Dog Registry");
Image image = new Image("/image/paw.png");
stage.getIcons().add(image);
stage.show();
stage.setOnCloseRequest(event -> {
event.consume();
exit(stage);
});
} catch(Exception e) {
e.printStackTrace();
}
}
public void exit(Stage stage){
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Exit");
alert.setHeaderText("You're about to exit!");
alert.setContentText("Are you sure you want to exit?: ");
if(alert.showAndWait().get() == ButtonType.OK){
System.out.println("You successfully exited!");
stage.close();
}
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
public class DogRegisterController implements Initializable {
@FXML
private TableView<Dog> dogTable;
@FXML
private TableColumn<Dog, Integer> idCol;
@FXML
private TableColumn<Dog, String> nameCol;
@FXML
private TableColumn<Dog, String> BreedCol;
@FXML
private TableColumn<Dog, Integer> ageCol;
@FXML
private TableColumn<Dog, Integer> weightCol;
@FXML
private TableColumn<Dog, Double> tailCol;
@FXML
private TextField idField;
@FXML
private TextField nameField;
@FXML
private TextField breedField;
@FXML
private TextField ageField;
@FXML
private TextField weightField;
@FXML
private Button removeBtn;
@FXML
private Button submitBtn;
@Override
public void initialize(URL location, ResourceBundle resources) {
idCol.setCellValueFactory(new PropertyValueFactory<Dog, Integer>("id"));
nameCol.setCellValueFactory(new PropertyValueFactory<Dog, String>("name"));
BreedCol.setCellValueFactory(new PropertyValueFactory<Dog, String>("breed"));
ageCol.setCellValueFactory(new PropertyValueFactory<Dog, Integer>("age"));
weightCol.setCellValueFactory(new PropertyValueFactory<Dog, Integer>("weight"));
tailCol.setCellValueFactory(new PropertyValueFactory<Dog, Double>("tailLength"));
setupTable();
}
@FXML
void submit(ActionEvent event){
Dog dog = new Dog(nameField.getText(), breedField.getText(),
Integer.parseInt(ageField.getText()), Integer.parseInt(weightField.getText()));
ObservableList<Dog> dogs = dogTable.getItems();
dogs.add(dog);
dogTable.setItems(dogs);
}
@FXML
void edit(ActionEvent event){
ObservableList<Dog> currentTableData = dogTable.getItems();
int currentDogId = Integer.parseInt(idField.getText());
for(Dog dog : currentTableData){
if(dog.getId() == currentDogId) {
dog.setId(Integer.parseInt(idField.getText()));
dog.setName(nameField.getText());
dog.setBreed(breedField.getText());
dog.setAge(Integer.parseInt(ageField.getText()));
dog.setWeight(Integer.parseInt(weightField.getText()));
dogTable.setItems(currentTableData);
dogTable.refresh();
idField.clear();
nameField.clear();
breedField.clear();
ageField.clear();
weightField.clear();
break;
}
}
}
@FXML
void rowClicked(MouseEvent event){
Dog clickedDog = dogTable.getSelectionModel().getSelectedItem();
idField.setText(String.valueOf(clickedDog.getId()));
nameField.setText(String.valueOf(clickedDog.getName()));
breedField.setText(String.valueOf(clickedDog.getBreed()));
ageField.setText(String.valueOf(clickedDog.getAge()));
weightField.setText(String.valueOf(clickedDog.getWeight()));
}
@FXML
void removeDog(ActionEvent event){
int selectedID = dogTable.getSelectionModel().getSelectedIndex();
dogTable.getItems().remove(selectedID);
idField.clear();
nameField.clear();
breedField.clear();
ageField.clear();
weightField.clear();
dogTable.refresh();
}
public static <S> void addAutoScroll(final TableView<S> dogTable) {
if (dogTable == null) {
throw new NullPointerException();
}
dogTable.getItems().addListener((ListChangeListener<S>) (c -> {
c.next();
final int size = dogTable.getItems().size();
if (size > 0) {
dogTable.scrollTo(size - 1);
}
}));
}
private void setupTable(){
Dog dog0 = new Dog("Luna", "Eurasier", 3, 22);
Dog dog1 = new Dog("Skye", "Dachshund", 4, 5);
Dog dog2 = new Dog("Bella", "Dachs", 2, 3);
Dog dog3 = new Dog("Sky", "Tax", 3, 3);
dogTable.getItems().addAll(dog0, dog1, dog2, dog3);
}
}
Dog.java
import java.util.concurrent.atomic.AtomicInteger;
public class Dog {
private static final AtomicInteger _ID = new AtomicInteger(0);
private String name;
private String breed;
private int age;
private int weight;
private double tailLength;
private int id;
public Dog(String name, String breed, int age, int weight){
this.id = _ID.incrementAndGet();
this.name = name;
this.breed = breed;
this.age = age;
this.weight = weight;
this.tailLength = getTailLength();
}
public double getTailLength(){
if (breed.toLowerCase().equals("tax") || breed.toLowerCase().equals("dachshund")|| breed.toLowerCase().equals("dachs")){
return 3.7;
} else {
return (age * weight) / 10.0;
}
}
public int getId() { return id; }
public void setId(int id){ this.id = id; }
public String getName() { return name; }
public void setName(String name){ this.name = name; }
public String getBreed() { return breed; }
public void setBreed(String breed){this.breed = breed;}
public int getWeight() { return weight; }
public void setWeight(int weight) { this.weight = weight; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
GUI.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane fx:id="mainPane" minHeight="900.0" minWidth="1200.0" prefHeight="900.0" prefWidth="1200.0" styleClass="pane" stylesheets="@StyleSheet.css" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="DogRegisterController">
<children>
<TableView fx:id="dogTable" layoutX="586.0" layoutY="160.0" onMouseClicked="#rowClicked" prefHeight="700.0" prefWidth="600.0" stylesheets="@StyleSheet.css" AnchorPane.rightAnchor="60.0" AnchorPane.topAnchor="100.0">
<columns>
<TableColumn fx:id="idCol" minWidth="50.0" prefWidth="50.0" text="ID" />
<TableColumn fx:id="nameCol" minWidth="140.0" prefWidth="140.0" text="Name" />
<TableColumn fx:id="BreedCol" minWidth="140.0" prefWidth="140.0" text="Breed" />
<TableColumn fx:id="ageCol" minWidth="80.0" text="Age" />
<TableColumn fx:id="weightCol" minWidth="80.0" text="Weight" />
<TableColumn fx:id="tailCol" minWidth="96.0" prefWidth="96.0" text="Tail-length" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
<styleClass>
<String fx:value="column-header" />
<String fx:value="column-header-background" />
<String fx:value="corner" />
<String fx:value="filler" />
<String fx:value="table-cell" />
<String fx:value="table-row-cell" />
<String fx:value="table-view" />
</styleClass>
</TableView>
<ImageView fitHeight="100.0" fitWidth="150.0" layoutX="220.0" layoutY="100.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@image/paw.png" />
</image>
</ImageView>
<VBox prefHeight="900.0" prefWidth="560.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="ID" textAlignment="CENTER">
<font>
<Font size="14.0" />
</font>
<VBox.margin>
<Insets left="100.0" right="100.0" top="260.0" />
</VBox.margin>
</Text>
<TextField fx:id="idField" editable="false" prefHeight="35.0" styleClass="text-field" stylesheets="@StyleSheet.css">
<VBox.margin>
<Insets bottom="5.0" left="100.0" right="100.0" top="5.0" />
</VBox.margin>
<padding>
<Insets left="10.0" />
</padding>
</TextField>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Name" textAlignment="CENTER">
<font>
<Font size="14.0" />
</font>
<VBox.margin>
<Insets left="100.0" right="100.0" />
</VBox.margin>
</Text>
<TextField fx:id="nameField" layoutX="10.0" layoutY="27.0" prefHeight="35.0" styleClass="text-field" stylesheets="@StyleSheet.css">
<VBox.margin>
<Insets bottom="5.0" left="100.0" right="100.0" top="5.0" />
</VBox.margin>
<padding>
<Insets left="10.0" />
</padding>
</TextField>
<Text layoutX="10.0" layoutY="39.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Breed" textAlignment="CENTER">
<font>
<Font size="14.0" />
</font>
<VBox.margin>
<Insets left="100.0" right="100.0" />
</VBox.margin>
</Text>
<TextField fx:id="breedField" layoutX="10.0" layoutY="127.0" prefHeight="35.0" styleClass="text-field" stylesheets="@StyleSheet.css">
<VBox.margin>
<Insets bottom="5.0" left="100.0" right="100.0" top="5.0" />
</VBox.margin>
<padding>
<Insets left="10.0" />
</padding>
</TextField>
<Text layoutX="10.0" layoutY="57.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Age" textAlignment="CENTER">
<font>
<Font size="14.0" />
</font>
<VBox.margin>
<Insets left="100.0" right="100.0" />
</VBox.margin>
</Text>
<TextField fx:id="ageField" layoutX="10.0" layoutY="144.0" prefHeight="35.0" styleClass="text-field" stylesheets="@StyleSheet.css">
<VBox.margin>
<Insets bottom="5.0" left="100.0" right="100.0" top="5.0" />
</VBox.margin>
<padding>
<Insets left="10.0" />
</padding>
</TextField>
<Text layoutX="10.0" layoutY="71.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Weight" textAlignment="CENTER">
<font>
<Font size="14.0" />
</font>
<VBox.margin>
<Insets left="100.0" right="100.0" />
</VBox.margin>
</Text>
<TextField fx:id="weightField" layoutX="10.0" layoutY="160.0" prefHeight="35.0" styleClass="text-field" stylesheets="@StyleSheet.css">
<VBox.margin>
<Insets bottom="5.0" left="100.0" right="100.0" top="5.0" />
</VBox.margin>
<padding>
<Insets left="10.0" />
</padding>
</TextField>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<Button fx:id="submitBtn" alignment="CENTER" mnemonicParsing="false" onAction="#submit" prefHeight="40.0" prefWidth="140.0" stylesheets="@../../Assignment-3/src/style.css" text="Submit">
<font>
<Font size="14.0" />
</font>
<HBox.margin>
<Insets left="25.0" right="25.0" top="10.0" />
</HBox.margin>
</Button>
<Button fx:id="editBtn" alignment="CENTER" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#edit" prefHeight="40.0" prefWidth="140.0" stylesheets="@../../Assignment-3/src/style.css" text="Edit">
<font>
<Font size="14.0" />
</font>
<HBox.margin>
<Insets left="25.0" right="25.0" top="10.0" />
</HBox.margin>
</Button>
</children>
<VBox.margin>
<Insets bottom="5.0" left="100.0" right="100.0" top="5.0" />
</VBox.margin>
</HBox>
</children>
</VBox>
<Button fx:id="removeBtn" alignment="CENTER" layoutX="770.0" layoutY="825.0" mnemonicParsing="false" onAction="#removeDog" prefHeight="40.0" prefWidth="140.0" stylesheets="@../../Assignment-3/src/style.css" text="Remove" AnchorPane.bottomAnchor="35.0">
<font>
<Font size="14.0" />
</font>
</Button>
</children>
</AnchorPane>
Solution
got it working thanks to @jewelsea
had forgotten to add "addAutoScroll(dogTable)" to setupTable method so the auto scroll was never called.
Controller.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
public class DogRegisterController implements Initializable {
public static <S> void addAutoScroll(final TableView<S> dogTable) {
if (dogTable == null) {
throw new NullPointerException();
}
dogTable.getItems().addListener((ListChangeListener<S>) (c -> {
c.next();
final int size = dogTable.getItems().size();
if (size > 0) {
dogTable.scrollTo(size - 1);
}
}));
}
private void setupTable(){
Dog dog0 = new Dog("Luna", "Eurasier", 3, 22);
Dog dog1 = new Dog("Skye", "Dachshund", 4, 5);
Dog dog2 = new Dog("Bella", "Dachs", 2, 3);
Dog dog3 = new Dog("Sky", "Tax", 3, 3);
dogTable.getItems().addAll(dog0, dog1, dog2, dog3);
addAutoScroll(dogTable);
}
}
Answered By - UnseenSnick
Answer Checked By - Mildred Charles (JavaFixing Admin)