[英]JavaFX - How to programmatically scroll a ScrollPane upon button click?
[英]In JavaFX how can I programmatically scroll a Scrollpane to the bottom?
我一直在為我和一些朋友開發一個小型聊天客戶端,自學Java和JFX 8。 不幸的是,由於某種原因,我無法以編程方式滾動到聊天消息所在的ScrollPane的底部。
我有一個方法,我簡單地調用scrollToBottom()執行以下代碼:
public void scrollToBottom() {
Platform.runLater(() -> this.getChatView().vvalueProperty().setValue(1.0));
}
我有一個按鈕,使用此方法滾動到底部,但任何其他編程方法(甚至從其他方法觸發按鈕)不能正確更新滾動窗格的滾動條。 但是,當我在滾動窗口上調用getVvalue()之后,它會返回正確的值,我試圖讓它滾動。 滾動窗格只是沒有滾動到所謂的值。
下面我有應用程序中唯一真正相關的類 - ChatBox類型只是VBox的擴展,它只使用擴展類型的Text。
public class MainScreenController {
//Lists
//Buttons
private Button logoutButton;
private Button btn1 = new Button("Test Button");
private Button scrollButton = new Button("Scroll Button");
//Numbers
//Booleans
private boolean isHosting;
//Strings
private String username = "";
//Scene
private Stage window;
private GridPane layout = new GridPane();
//Other Objects
private TextArea chatField = new TextArea();
private Label usernameLabel;
private TextArea usersArea = new TextArea("Connected users: ");
private VBox firstColumn = new VBox(10);
private VBox secondColumn = new VBox(10);
private ImageView mediaColumn = new ImageView();
private Server server = new Server();
private Client client = new Client();
private TextField dlField = new TextField();
private AudioHandler audioHandler = new AudioHandler();
private ChatBox chatBox = new ChatBox();
private ScrollPane chatView = new ScrollPane(this.chatBox);
private HBox buttonBox = new HBox(10);
private void initChatView() {
this.getChatView().setMinSize(500, 500);
this.getChatView().setPrefSize(500, 500);
this.getChatView().setMaxSize(500, 500);
this.getChatView().setStyle("-fx-focus-color: transparent; -fx-background-color: gainsboro");
this.chatView.hbarPolicyProperty().set(ScrollBarPolicy.NEVER);
this.chatView.vbarPolicyProperty().set(ScrollBarPolicy.AS_NEEDED);
/*this.chatView.vvalueProperty().addListener(e -> {
if(this.chatView.getVvalue() != 1) {
System.out.println("Pre: " + this.chatView.getVvalue());
this.chatView.vvalueProperty().set(1);
System.out.println("Post: " + this.chatView.getVvalue());
}
});*/
this.scrollButton.setOnAction(e -> {
scrollToBottom();
});
}
private void initSecondColumn() {
this.secondColumn.setStyle("-fx-background-color: gainsboro");
this.secondColumn.setPrefSize(525, 550);
this.secondColumn.setMinSize(450, 550);
this.secondColumn.setMaxSize(525, 550);
Button dlButton = new Button("Download link:");
dlButton.setStyle("-fx-focus-color: transparent");
dlButton.setOnAction(e -> {
if (!this.dlField.getText().equals(null) || !this.dlField.getText().equals(null)) {
try {
FileHandler.downloadFile(this.window, this.dlField.getText());
} catch (Exception ex) {
ex.printStackTrace();
}
this.dlField.clear();
}
});
this.dlField.setPrefSize(450, 20);
this.dlField.setMinSize(450, 20);
this.dlField.setMaxSize(450, 20);
this.mediaColumn.setFitHeight(475);
this.mediaColumn.setPreserveRatio(true);
this.mediaColumn.setStyle("-fx-border-color: red");
this.mediaColumn.prefWidth(450);
this.mediaColumn.prefHeight(475);
this.mediaColumn.minWidth(450);
this.mediaColumn.minHeight(475);
this.mediaColumn.maxWidth(450);
this.mediaColumn.maxHeight(475);
this.secondColumn.getChildren().addAll(dlButton, this.dlField, mediaColumn);
}
private void initUsersArea() {
this.usersArea.setPrefSize(500, 75);
this.usersArea.setMinSize(500, 75);
this.usersArea.setMaxSize(500, 75);
this.usersArea.setEditable(false);
this.usersArea.setStyle("-fx-focus-color: transparent; -fx-background-color: gainsboro");
}
public void scrollToBottom() {
Platform.runLater(() -> this.getChatView().vvalueProperty().setValue(1.0));
System.out.println(this.chatView.getVvalue());
}
public MainScreenController(GridPane layout, Stage window, Scene currentScene, Scene nextScene, WindowController windowController) throws URISyntaxException, IOException {
new File(FileHandler.downloadsPath).mkdirs();
this.layout = layout;
this.window = window;
this.initChatView();
FileHandler.readLog(this.getChatBox());
}
private void initUsernameLabel() {
this.usernameLabel = new Label();
this.usernameLabel.setStyle("-fx-border-color: black; -fx-background-color: silver; -fx-focus-color: transparent");
this.usernameLabel.setText(" Logged in as ");
this.usernameLabel.setTextFill(Color.BLUE);
}
private void initLayout() {
this.layout.setPadding(new Insets(10, 10, 10, 10));
this.layout.setVgap(10);
this.layout.setHgap(10);
this.layout.getChildren().addAll(this.firstColumn, this.secondColumn);
GridPane.setColumnIndex(this.firstColumn, 0);
GridPane.setColumnIndex(this.secondColumn, 1);
GridPane.setValignment(secondColumn, VPos.BOTTOM);
this.buttonBox.getChildren().addAll(this.logoutButton, this.scrollButton);
this.firstColumn.getChildren().addAll(this.buttonBox, this.usernameLabel, this.usersArea, this.chatView, this.getChatField());
}
public void initBtn1() {
btn1.setStyle("-fx-focus-color: transparent");
GridPane.setConstraints(btn1, 1, 0);
GridPane.setValignment(btn1, VPos.BASELINE);
this.layout.getChildren().add(btn1);
btn1.addEventHandler(ActionEvent.ACTION, e -> {
boolean b = false;
if (!b) {
audioHandler.startRecording();
b = !b;
} else if (b) {
audioHandler.stopRecording();
b = !b;
}
});
}
public void addMessage(String msg, String color) {
FileHandler.writeToChatLog(msg);
if (!msg.startsWith("*!") && !msg.startsWith("/")) {
Platform.runLater(() -> {
this.getChatBox().addText(new ChatText(msg, color));
this.scrollButton.arm();
this.scrollButton.fire();
});
}
}
private void initChatField() {
this.getChatField().setPrefSize(500, 10);
this.getChatField().setMaxHeight(10);
this.getChatField().autosize();
this.getChatField().setWrapText(true);
this.getChatField().addEventHandler(KeyEvent.KEY_PRESSED, key -> {
if (key.getCode() == KeyCode.ENTER) {
key.consume();
if (this.getChatField().getText().startsWith("/")) {
CommandParser.parse(this.getChatField().getText(), this);
} else {
try {
this.getClient().getClientSendingData().writeUTF(this.getChatField().getText().trim());
this.getChatField().clear();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
this.getChatField().setStyle("-fx-background-color: gainsboro");
}
private void initLogoutButton() {
this.logoutButton = new Button();
this.logoutButton.setText("Log out");
this.logoutButton.setOnAction(e -> {
try {
this.client.getClientSendingData().writeUTF("*![System] " + SystemInfo.getDate() + ": " + this.client.getClientName() + " has disconnected.");
System.out.println("Logging out.");
window.close();
new ChatClient().start(new Stage());
} catch (Exception ex) {
ex.printStackTrace();
}
});
this.logoutButton.setStyle("-fx-focus-color: transparent");
}
public void initMainScreen() {
initUsernameLabel();
initLogoutButton();
initChatField();
initLayout();
initUsersArea();
initBtn1();
initSecondColumn();
}
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return this.username;
}
public Label getUsernameLabel() {
return this.usernameLabel;
}
public GridPane getLayout() {
return this.layout;
}
public void setIsHosting(boolean b) {
this.isHosting = b;
}
public boolean getIsHosting() {
return this.isHosting;
}
public Server getServer() {
return this.server;
}
public void setServer(Server hostServer) {
this.server = hostServer;
}
public Client getClient() {
return this.client;
}
public void setClient(Client client) {
this.client = client;
}
public ScrollPane getChatView() {
return chatView;
}
public void setChatView(ScrollPane chatView) {
this.chatView = chatView;
}
public TextArea getChatField() {
return chatField;
}
public void setChatField(TextArea chatField) {
this.chatField = chatField;
}
public TextArea getUsersArea() {
return this.usersArea;
}
public ChatBox getChatBox() {
return chatBox;
}
public void setChatBox(ChatBox chatBox) {
this.chatBox = chatBox;
}
}
如果您有任何其他一般性建議(我剛剛學習Java),歡迎提供所有提示。 :) 謝謝!
我將TextArea中的文本滾動到底部的方法是,將文本中的插入符號設置為文本的末尾,如:
textArea.positionCaret(textArea.getText().length());
實際上,為了確保TextArea中的文本不向左水平滾動(例如,當TextArea中的最后一行非常長時),我將插入符號設置在文本中最后一個“\\ n”后面,像這樣:
String content = textArea.getText();
int indexOfLastLineFeed = content.lastIndexOf("\n");
if (indexOfLastLineFeed == -1)
textArea.positionCaret(content.length());
else
textArea.positionCaret(Math.min(indexOfLastLineFeed + 1, content.length()));
那個錯誤也發生在我身上,同時將ScrollPane與MasonryPane(來自JFoenix)混淆。 我找到的解決方案是調用scrollpane的layout()方法及其內容。 層次結構:ScrollPane - > MasonryPane`
masonryPane.getChildren().addListener((ListChangeListener<Node>) c -> {
masonryPane.layout();
scrollPane.layout();
scrollPane.setVvalue(1.0f);
});
我發現問題是由多線程引起的。 對於任何感興趣的人,嘗試在執行scrollToBottom()調用的線程上添加一個非常小的Thread.sleep(); 我用10毫秒作為最低似乎工作。 感謝任何試圖提供幫助的人。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.