Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement tags bar in JavaFX

Tags:

java

tags

javafx

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

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

Sample Tags Image

Actual Question before answered:(asked May 22 at 19:53)

The title might be not too great but what I want to do is something like this in JavaFX:

Examples

YouTube:

YouTube tags example

StackOverFlow(which has and autocomplete):

StackOverflow tags example

Question: I don't require to write me the code for that. Instead I want to know how I can achieve that using JavaFX and some ideas.

like image 913
GOXR3PLUS Avatar asked May 22 '16 19:05

GOXR3PLUS


2 Answers

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();
}
like image 106
fabian Avatar answered Oct 03 '22 04:10

fabian


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

}

enter image description here

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);
}
like image 20
Vladlen Gladis Avatar answered Oct 03 '22 03:10

Vladlen Gladis