【问题标题】:JavaFX: Make node take no space, but let its parent decide its positionJavaFX:使节点不占用空间,但让其父节点决定其位置
【发布时间】:2015-04-30 13:55:28
【问题描述】:

我有一个 TextField 和一个 ListView。当用户在 TextField 中键入时,ListView 中会出现建议:

当 TextField 为空时,ListView 消失,通过将可见和托管属性设置为 false。

但是,当用户开始键入时,ListView 会占用空间并将所有内容向下推。使用 .setManaged(false) 允许它不占用任何空间,但它不再显示,因为我还没有为它定义一个位置。我试过设置搜索列表的layoutX和layoutY,还是不显示。

理想情况下,我希望 ListView 的位置受布局影响,但不占用任何空间。

有什么想法吗?

【问题讨论】:

  • 相关:JavaFX 2 custom popup pane。这个答案可能有点过时了,在 JavaFX 8+ 中可能有更好的方法来完成这种行为。
  • 我遇到了同样的问题,并尝试了与您相同的方法。管理设置为false时列表不可见的问题解决了吗?

标签: java layout javafx overlap


【解决方案1】:

将包含文本字段的容器包装在 AnchorPane 中。在文本字段容器之后将ListView 添加到AnchorPane(使其保持在顶部)。然后,当您使其可见时,您需要将ListView 相对于文本字段适当地定位;我认为最好的方法是首先将文本字段的边界从本地坐标转换为Scene 坐标,然后将这些边界转换为相对于AnchorPane 的坐标。

这是一个 SSCCE:

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class SuggestionList extends Application {

    @Override
    public void start(Stage primaryStage) {
        AnchorPane root = new AnchorPane();

        ListView<String> suggestionBox = new ListView<>();
        suggestionBox.getItems().addAll("Here", "Are", "Some", "Suggestions");
        suggestionBox.setMaxHeight(100);
        suggestionBox.setVisible(false);

        // Grid pane to hold a bunch of text fields:
        GridPane form = new GridPane();
        for (int i=0; i<10; i++) {
            form.addRow(i, new Label("Enter Text:"), createTextField(suggestionBox));
        }

        // just move the grid pane a little to test suggestion box positioning:
        AnchorPane.setLeftAnchor(form, 20.0);
        AnchorPane.setRightAnchor(form, 20.0);
        AnchorPane.setTopAnchor(form, 20.0);
        AnchorPane.setBottomAnchor(form, 20.0);

        // allows focus on grid pane, so user can click on it to remove focus from text field.
        form.setFocusTraversable(true);

        root.setPadding(new Insets(20));

        root.getChildren().addAll(form, suggestionBox);

        Scene scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TextField createTextField(ListView<String> suggestionBox) {
        TextField textField = new TextField();
        ChangeListener<String> selectionListener = (obs, oldItem, newItem) -> {
            if (newItem != null) {
                textField.setText(newItem);
            }
        };
        textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
            if (isNowFocused) {
                suggestionBox.setVisible(true);

                // compute bounds of text field relative to suggestion box's parent:
                Parent parent = suggestionBox.getParent(); // (actually the anchor pane)
                Bounds tfBounds = textField.getBoundsInLocal();
                Bounds tfBoundsInScene = textField.localToScene(tfBounds);
                Bounds tfBoundsInParent = parent.sceneToLocal(tfBoundsInScene);

                // position suggestion box:
                suggestionBox.setLayoutX(tfBoundsInParent.getMinX());
                suggestionBox.setLayoutY(tfBoundsInParent.getMaxY());
                suggestionBox.setPrefWidth(tfBoundsInParent.getWidth());

                suggestionBox.getSelectionModel().selectedItemProperty().addListener(selectionListener);
            } else {
                suggestionBox.setVisible(false);
                suggestionBox.getSelectionModel().selectedItemProperty().removeListener(selectionListener);
            }
        });
        textField.setOnAction(event -> {
            suggestionBox.setVisible(false);
            suggestionBox.getSelectionModel().selectedItemProperty().removeListener(selectionListener);         
        });
        return textField ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

您也许可以使用类似的定位技巧并将其添加到同一个场景中,将managed 设置为 false。

【讨论】:

  • 谢谢,这正是我所需要的,但我似乎无法让它与我的程序一起工作。 AnchorPane 的确切用途是什么?例如,我不能只使用 VBox 吗?您的代码不完全适合我的场景,因为我为每个文本字段显示不同的建议框。
  • VBox 无法工作,因为它定位了它的子节点,因此一个垂直显示在另一个之上。您需要使用允许您自己定位子节点的窗格,这就是我使用 AnchorPane 的原因。您可以轻松调整代码,以便为每个文本字段创建不同的建议框。
  • 我已经设法让它几乎可以工作了.. 但它目前显示在 TextField 正下方的表格后面:i.imgur.com/Jpc0tLQ.png。生成它的 FXML 如下:pastie.org/9992170。 TableView 是否必须是 ListView 的同级才能工作?我也设法在不使用 AnchorPane 的情况下将它带到了这个阶段,所以我仍然不完全确定。
猜你喜欢
  • 1970-01-01
  • 2021-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-11
  • 2015-07-30
  • 2020-08-30
相关资源
最近更新 更多