Implement tags bar in JavaFX

后端 未结 4 1511
无人及你
无人及你 2020-12-01 09:38

Demonstration of answer:(answered May 29 at 3:10 am)

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

Actual Que

相关标签:
4条回答
  • 2020-12-01 10:13

    For the tags you can use a custom styled HBox containing a Text (the tag name) node an a Button (the deletion button (X)). By playing around with the background and the border you can achieve the desired look of the tags.

    The onAction handler of the button should remove the tag from it's parent...

    For the whole tag bar you can use another HBox. Use the appropriate border for the correct look. In addition to the tags add a TextField with no background as last element and set the Hgrow property of that TextField to Priotity.ALWAYS to cover the rest of the available space.

    The onAction handler of this TextField adds new tags and clears the content of the TextField.

    You could e.g. use ControlsFX's autocompletion features with the TextField or implement it on your own for a custom look...

    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);
            }
        }
    
    }
    

    style.css

    .tag-bar {
        -fx-border-color: blue;
        -fx-spacing: 3;
        -fx-padding: 3;
        -fx-max-height: 30;
    }
    
    .tag-bar .tag {
        -fx-background-color: lightblue;
        -fx-alignment: center;
    }
    
    .tag-bar .tag .button {
        -fx-background-color: transparent;
    }
    
    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button("Sort");
    
        StackPane.setAlignment(btn, Pos.BOTTOM_CENTER);
    
        TagBar tagBar = new TagBar();
    
        btn.setOnAction((ActionEvent event) -> {
            FXCollections.sort(tagBar.getTags());
        });
    
        Button btn2 = new Button("add \"42\"");
        btn2.setOnAction(evt -> {
            if (!tagBar.getTags().contains("42")) {
                tagBar.getTags().add("42");
            }
        });
    
        VBox root = new VBox();
        root.getChildren().addAll(tagBar, btn, btn2);
        root.setPrefSize(300, 400);
    
        Scene scene = new Scene(root);
    
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    0 讨论(0)
  • 2020-12-01 10:28

    Simple implementation of this code!

    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);
    }
    

    }

    Also if u need different events for click on tag and click on "X" you can implement tagButton like this :

    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);
    }
    
    0 讨论(0)
  • 2020-12-01 10:29

    This is my version too

    The whole Main class

    its somehow long that's why.

    But to sum up. You need a

    1: FlowPane for the container, and you do not have to worry about wrapping,it will wrap itself, both vertical or horizontal.

    2: Label of course for your Text, which has a GraphicProperty

    3: Path - well you could use Button, and add a Shape or Image to it, but that will be a lot of Nodes, so i used Path and i drew a X red button.

    The rest is styling to your preferred color

    EDIT something like this?

    you can style it to get that output

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

    use this line on the TextField

    0 讨论(0)
  • 2020-12-01 10:33

    Here is a basic example of a tag bar (I wrote some code, because I think it's easier to follow). For the additional AutoComplete function you could use e.g. ControlsFx, as fabian already mentioned.

    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);
        }
    
    } 
    
    0 讨论(0)
提交回复
热议问题