java - Does JavaFX have a component with flow columns and possibility to select an item? - Stack Overflow

I have a list of N items and I need them to be displayed in columns like in vertical TilePane. But I al

I have a list of N items and I need them to be displayed in columns like in vertical TilePane. But I also need to select them via keyboard and mouse, to get selected item and to scroll to the selected item. Something like this:

Could anyone say what JavaFX component can be used in such case? I thought about TilePane but it doesn't have a lot of features I need.

I have a list of N items and I need them to be displayed in columns like in vertical TilePane. But I also need to select them via keyboard and mouse, to get selected item and to scroll to the selected item. Something like this:

Could anyone say what JavaFX component can be used in such case? I thought about TilePane but it doesn't have a lot of features I need.

Share Improve this question asked Mar 12 at 10:05 SilverCubeSilverCube 6443 silver badges12 bronze badges 4
  • 3 Looks like you want the JavaFX equivalent of Swing's JList with explicit layout orientation. – Abra Commented Mar 12 at 10:22
  • @Abra I don't have much experience with Swing, but it seems that you are right. – SilverCube Commented Mar 12 at 11:11
  • 3 There is no component in JavaFX with that functionality. If I remember correctly, ControlsFX has a GridView, but I don't think it has selection. You will likely have to implement at least some of the functionality yourself. – James_D Commented Mar 12 at 12:37
  • If you implement this yourself then I recommend using a FlowPane instead of a TilePane. The latter can lead to a lot of wasted space if you have an item that's significantly larger than the others. Of course, if you expect to display many items then you might want to look into making your custom control "virtualized" (e.g., like how a ListView only displays a small number of cells and reuses them). – Slaw Commented Mar 13 at 0:26
Add a comment  | 

1 Answer 1

Reset to default 3

If you are trying to implement by FlowPane, below is an approach that you can get some initial idea.

The general idea is to create a custom FlowPane that:

  • wraps the provided nodes with some StackPane that highlights the selection

  • have key events to listen for UP/Down

  • selected index to highlight the wrapper based on click or key events.

class SelectableFlowPane extends FlowPane {
    private IntegerProperty selectedIndex = new SimpleIntegerProperty();
    private PseudoClass selectedPseudo = PseudoClass.getPseudoClass("selected");

    public SelectableFlowPane() {
        selectedIndex.addListener((obs, old, val) -> {
            if (old.intValue() > -1) {
                getChildren().get(old.intValue()).pseudoClassStateChanged(selectedPseudo, false);
            }
            if (val.intValue() > -1) {
                getChildren().get(val.intValue()).pseudoClassStateChanged(selectedPseudo, true);
            }
        });
        
        addEventFilter(KeyEvent.KEY_PRESSED, e -> {
            if (selectedIndex.get() < 0) {
                return;
            }
            KeyCode code = e.getCode();
            if (code == KeyCode.DOWN) {
                int nextIndex = selectedIndex.get() + 1 < getChildren().size() ? selectedIndex.get() + 1 : 0;
                selectedIndex.set(nextIndex);
            } else if (code == KeyCode.UP) {
                int nextIndex = selectedIndex.get() - 1 < 0 ? getChildren().size() - 1 : selectedIndex.get() - 1;
                selectedIndex.set(nextIndex);
            }
        });
    }

    public void addChildren(List<? extends Node> nodes) {
        nodes.forEach(n -> {
            StackPane outer = new StackPane(n);
            outer.setOnMouseClicked(e -> {
                selectedIndex.set(getChildren().indexOf(outer));
            });
            outer.getStyleClass().add("outer");
            getChildren().add(outer);
        });
    }
}

You can check the full working demo below. There is lot of scope to fine tune this code :)

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.css.PseudoClass;
import javafx.event.Event;
import javafx.event.EventDispatcher;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

public class SelectableFlowPaneDemo extends Application {
    SecureRandom rnd = new SecureRandom();
    public static final String CSS = "data:text/css," + // language=CSS
            """
                    .label{
                       -fx-font-size: 15px;
                       -fx-font-weight: bold;
                       -fx-padding: 8px 4px;
                    }
                    .outer{
                        -fx-alignment: CENTER_LEFT;
                    }
                    .outer:hover{
                       -fx-background-color: #AAAAAA50;
                    }
                    .outer:selected{
                       -fx-background-color: #99d1ff, #cce8ff;
                       -fx-background-insets: 0, 1;
                    }
                    """;

    @Override
    public void start(Stage primaryStage) throws Exception {
        SelectableFlowPane flowPane = new SelectableFlowPane();
        List<Label> items = new ArrayList<>();
        IntStream.range(1, 100).forEach(i -> {
            String t = rnd.nextInt() % 2 == 0 ? "Item " : "";
            items.add(new Label("Item " + t + rnd.nextInt(256589)));
        });
        flowPane.addChildren(items);
        flowPane.setOrientation(Orientation.VERTICAL);
        flowPane.setHgap(10);
        flowPane.setHgap(10);
        flowPane.setPadding(new Insets(10));

        ScrollPane root = new ScrollPane(flowPane);
        EventDispatcher scrollPaneEventDispatcher = root.getEventDispatcher();
        root.setEventDispatcher((event, tail) -> {
            Event eventToDispatch = scrollPaneEventDispatcher.dispatchEvent(event, tail);
            if (KeyEvent.KEY_PRESSED.equals(event.getEventType())) {
                if (KeyCode.UP.equals(((KeyEvent) event).getCode()) || KeyCode.DOWN.equals(((KeyEvent) event).getCode())) {
                    if (eventToDispatch == null) {
                        return event;
                    }
                }
            }
            return eventToDispatch;
        });
        flowPane.selectedIndex.addListener((obs, old, val) -> {
            if (val.intValue() > -1) {
                ensureVisible(root, flowPane.getChildren().get(val.intValue()));
            }
        });
        root.setFitToHeight(true);
        Scene scene = new Scene(root, 500, 500);
        scene.getStylesheets().add(CSS);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Selectable FlowPane Demo");
        primaryStage.show();
    }

    private static void ensureVisible(ScrollPane pane, Node node) {
        double width = pane.getContent().getBoundsInLocal().getWidth();
        double height = pane.getContent().getBoundsInLocal().getHeight();
        double x = node.getBoundsInParent().getMaxX();
        double y = node.getBoundsInParent().getMaxY();

        pane.setVvalue(y / height);
        pane.setHvalue(x / width);
        node.requestFocus();
    }

    class SelectableFlowPane extends FlowPane {
        private IntegerProperty selectedIndex = new SimpleIntegerProperty();
        private PseudoClass selectedPseudo = PseudoClass.getPseudoClass("selected");

        public SelectableFlowPane() {
            selectedIndex.addListener((obs, old, val) -> {
                if (old.intValue() > -1) {
                    getChildren().get(old.intValue()).pseudoClassStateChanged(selectedPseudo, false);
                }
                if (val.intValue() > -1) {
                    getChildren().get(val.intValue()).pseudoClassStateChanged(selectedPseudo, true);
                }
            });

            addEventFilter(KeyEvent.KEY_PRESSED, e -> {
                if (selectedIndex.get() < 0) {
                    return;
                }
                KeyCode code = e.getCode();
                if (code == KeyCode.DOWN) {
                    int nextIndex = selectedIndex.get() + 1 < getChildren().size() ? selectedIndex.get() + 1 : 0;
                    selectedIndex.set(nextIndex);
                } else if (code == KeyCode.UP) {
                    int nextIndex = selectedIndex.get() - 1 < 0 ? getChildren().size() - 1 : selectedIndex.get() - 1;
                    selectedIndex.set(nextIndex);
                }
            });
        }

        public void addChildren(List<? extends Node> nodes) {
            nodes.forEach(n -> {
                StackPane outer = new StackPane(n);
                outer.setOnMouseClicked(e -> {
                    selectedIndex.set(getChildren().indexOf(outer));
                });
                outer.getStyleClass().add("outer");
                getChildren().add(outer);
            });
        }
    }
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744759925a4592112.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信