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.
1 Answer
Reset to default 3If 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
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:37FlowPane
instead of aTilePane
. 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 aListView
only displays a small number of cells and reuses them). – Slaw Commented Mar 13 at 0:26