在JavaFX中实现标签栏

时间:2016-05-22 19:53:51

标签: java javafx tags

答案示范:( 5月29日凌晨3:10回答)

**10/7/2016** you can find the code on GitHub

Sample Tags Image

回答前的实际问题:( 5月22日19:53问)

标题可能不是很好,但我想要做的是在JavaFX中这样的事情:

  

实施例

的YouTube

YouTube tags example

StackOverFlow(具有并自动完成)

StackOverflow tags example

问题: 我不需要写代码。相反,我想知道如何使用JavaFX和一些想法实现这一点。

4 个答案:

答案 0 :(得分:12)

对于标签,您可以使用包含HBox(标签名称)节点的自定义样式TextButton(删除按钮(X))。通过使用背景和边框,您可以获得所需的标签外观。

按钮的onAction处理程序应从其父级中移除标记...

对于整个标记栏,您可以使用其他HBox。使用适当的边框以获得正确的外观。除了代码之外,添加一个没有背景的TextField作为最后一个元素,并将Hgrow的{​​{1}}属性设置为TextField以覆盖剩余的可用空间。< / p>

Priotity.ALWAYS的{​​{1}}处理程序会添加新标记并清除onAction的内容。

你可以,例如使用ControlsFX与TextField的自动完成功能,或者自己实现自定义外观......

TextField

的style.css

TextField
public class TagBar extends HBox {

    private final ObservableList<String> tags;
    private final TextField inputTextField;

    public ObservableList<String> getTags() {
        return tags;
    }

    public TagBar() {
        getStyleClass().setAll("tag-bar");
        getStylesheets().add(getClass().getResource("style.css").toExternalForm());
        tags = FXCollections.observableArrayList();
        inputTextField = new TextField();
        inputTextField.setOnAction(evt -> {
            String text = inputTextField.getText();
            if (!text.isEmpty() && !tags.contains(text)) {
                tags.add(text);
                inputTextField.clear();
            }
        });

        inputTextField.prefHeightProperty().bind(this.heightProperty());
        HBox.setHgrow(inputTextField, Priority.ALWAYS);
        inputTextField.setBackground(null);

        tags.addListener((ListChangeListener.Change<? extends String> change) -> {
            while (change.next()) {
                if (change.wasPermutated()) {
                    ArrayList<Node> newSublist = new ArrayList<>(change.getTo() - change.getFrom());
                    for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
                        newSublist.add(null);
                    }
                    for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
                        newSublist.set(change.getPermutation(i), getChildren().get(i));
                    }
                    getChildren().subList(change.getFrom(), change.getTo()).clear();
                    getChildren().addAll(change.getFrom(), newSublist);
                } else {
                    if (change.wasRemoved()) {
                        getChildren().subList(change.getFrom(), change.getFrom() + change.getRemovedSize()).clear();
                    }
                    if (change.wasAdded()) {
                        getChildren().addAll(change.getFrom(), change.getAddedSubList().stream().map(Tag::new).collect(Collectors.toList()));
                    }
                }
            }
        });
        getChildren().add(inputTextField);
    }

    private class Tag extends HBox {

        public Tag(String tag) {
            getStyleClass().setAll("tag");
            Button removeButton = new Button("X");
            removeButton.setOnAction((evt) -> tags.remove(tag));
            Text text = new Text(tag);
            HBox.setMargin(text, new Insets(0, 0, 0, 5));
            getChildren().addAll(text, removeButton);
        }
    }

}

答案 1 :(得分:6)

简单实现此代码!

import ....

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{

        BorderPane root = new BorderPane();
        HBox tagsPane = new HBox(10);
        tagsPane.setStyle("-fx-border-color: #F1F1F1;" +
                "          -fx-border-width: 1px;" +
                "          -fx-border-radius: 10;" +
                "          -fx-border-insets: 5");
        root.setBottom(tagsPane);

        TextField textField = new TextField();
        textField.setPromptText("Tag name - ENTER to add");
        textField.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) {
                tagButton(tagsPane, textField.getText());
                textField.clear();
            }
        });

        root.setTop(textField);

        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 450, 275));
        primaryStage.show();
    }


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

    //little image as 15x15 for example
    Image toUse = new Image("sample/delete.png");

    //box is the pane where this buttons will be placed
    public void tagButton(HBox box,String tag){
        ImageView closeImg = new ImageView(toUse);
        Button result = new Button(tag,closeImg);
        result.setPrefHeight(20);
        result.setContentDisplay(ContentDisplay.RIGHT);

        result.setOnAction(event -> box.getChildren().remove(result));
        box.getChildren().add(result);
}

}

enter image description here

此外,如果您需要不同的事件来点击标签并单击“X”,您可以像这样实现tagButton:

public void tagButton(HBox box,String tag){
    ImageView closeImg = new ImageView(toUse);
    HBox button = new HBox();
    button.setStyle("-fx-padding:4;" +
            "        -fx-border-width: 2;" +
            "        -fx-border-color: black;" +
            "        -fx-border-radius: 4;" +
            "        -fx-background-color: f1f1f1;" +
            "        -fx-border-insets: 5;");
    button.setPrefHeight(20);
    button.getChildren().addAll(new Label(tag),closeImg);

    closeImg.setOnMouseClicked(event -> 
            box.getChildren().remove(button)
    );
    button.setOnMouseClicked(event -> {
        //doSomethig
    });

    box.getChildren().add(button);
}

答案 2 :(得分:4)

这也是我的版本

enter image description here

enter image description here

enter image description here

The whole Main class

它有点长,这就是原因。

但总结一下。你需要一个

1:FlowPane用于容器,你不必担心包装,它会自行包裹,包括垂直或水平。

2:Label当然是您的文字,其中有GraphicProperty

3:Path - 您可以使用Button,并为其添加ShapeImage,但这将是很多节点,因此我使用了Path我画了一个X红色按钮。

其余的是造型为您喜欢的颜色

修改 这样的事情?

enter image description here

你可以设置它以获得输出

setFont(Font.font("Serif Regular", FontWeight.SEMI_BOLD,12));

TextField

上使用此行

答案 3 :(得分:3)

这是一个标签栏的基本示例(我写了一些代码,因为我觉得它更容易理解)。对于其他自动完成功能,您可以使用例如ControlsFx,正如fabian已经提到的那样。

public class CloseTag extends HBox implements Comparable<CloseTag> {

    private Label     label;
    private Label     closeIcon;

    public CloseTag(String text) {
        setStyle("-fx-padding:8;");

        Text icon = GlyphsDude.createIcon(FontAwesomeIcon.TIMES_CIRCLE);
        closeIcon = new Label(null, icon);

        label = new Label(text, new StackPane(closeIcon));
        label.setContentDisplay(ContentDisplay.RIGHT);
        getChildren().add(label);
    }

    public void setOnCloseAction(EventHandler<? super MouseEvent> action) {
        closeIcon.setOnMouseClicked(action);
    }

    public String getText() {
        return label.getText();
    }

    @Override
    public int compareTo(CloseTag other) {
        return getText().compareTo(other.getText());
    }
}



public class TagPane extends FlowPane {

    private TextField textField;

    public TagPane() {
        setStyle("-fx-padding:8;" + "-fx-hgap:10;");
        setOnMouseClicked(evt -> onMouseClickedd(evt));

        textField = new TextField();
        textField.setOnKeyPressed(evt -> onKeyPressed(evt, textField));
    }

    private void onMouseClickedd(MouseEvent mouseEvent) {
        if (mouseEvent.getTarget() != this || textField.getParent() != null ) {
            return;
        }

        getChildren().add(textField);
        textField.requestFocus();
    }

    private void onKeyPressed(KeyEvent evt, TextField textField) {
        if (evt.getCode() == KeyCode.ENTER || evt.getCode() == KeyCode.TAB) {
            createTag(textField.getText());
            textField.clear();
        }
    }

    private void createTag(String text) {
        CloseTag tag = new CloseTag(text);
        tag.setOnCloseAction(evt -> removeTag(tag));
        getChildren().remove(textField);
        getChildren().add(tag);
    }

    private void removeTag(CloseTag tag) {
        getChildren().remove(tag);
    }

}