JavaFX-element ListView z przyciskiem Image
Chciałbym wiedzieć, czy jest możliwość dodania przycisku obrazka po każdym elemencie ListView. Na przykład:
Gdzie te czerwone kwadraty powinny mieć zamiast tego przycisk. Jeśli jest to możliwe, Jak mogę obsłużyć Zdarzenie kliknięcia przycisku elementu?
EDIT: jeśli jest inna kontrola, która może to zrobić, proszę daj mi znać. Testuję TableView.
Z góry dzięki!
3 answers
Testowałem to ostatnio. Moje rozwiązanie:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SO extends Application {
static class XCell extends ListCell<String> {
HBox hbox = new HBox();
Label label = new Label("(empty)");
Pane pane = new Pane();
Button button = new Button("(>)");
String lastItem;
public XCell() {
super();
hbox.getChildren().addAll(label, pane, button);
HBox.setHgrow(pane, Priority.ALWAYS);
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println(lastItem + " : " + event);
}
});
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(null); // No text in label of super class
if (empty) {
lastItem = null;
setGraphic(null);
} else {
lastItem = item;
label.setText(item!=null ? item : "<null>");
setGraphic(hbox);
}
}
}
@Override
public void start(Stage primaryStage) throws Exception {
StackPane pane = new StackPane();
Scene scene = new Scene(pane, 300, 150);
primaryStage.setScene(scene);
ObservableList<String> list = FXCollections.observableArrayList(
"Item 1", "Item 2", "Item 3", "Item 4");
ListView<String> lv = new ListView<>(list);
lv.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> param) {
return new XCell();
}
});
pane.getChildren().add(lv);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Komórki będą wyglądać tak:
Odpowiednią częścią jest Metoda XCell.updateItem
i wywołanie setGraphic
. Z setGraphic()
zazwyczaj ustawia się ikonę dla etykiety, ale równie dobrze sprawdza się ustawienie węzła złożonego - w tym przypadku HBox z etykietą i przyciskiem.
Musisz się upewnić, że obsługa zdarzeń przycisku odnosi się do właściwej pozycji na liście. W pierwszym linku poniżej jest mowa, że obecnie wybrany element może na razie wystarczyć. Pobieramy więc aktualnie wybrany indeks z listy podczas obsługi zdarzenia przycisku.
Możesz spojrzeć na te:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-28 20:02:53
Możesz również użyć listy niestandardowych obiektów HBox. Poniższy przykład pokazuje użycie takiej niestandardowej klasy wewnętrznej HBox oraz proces zagnieżdżania listy takich obiektów do kontrolki ListView.
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
public class ListViewDemo extends Application {
public static class HBoxCell extends HBox {
Label label = new Label();
Button button = new Button();
HBoxCell(String labelText, String buttonText) {
super();
label.setText(labelText);
label.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(label, Priority.ALWAYS);
button.setText(buttonText);
this.getChildren().addAll(label, button);
}
}
public Parent createContent() {
BorderPane layout = new BorderPane();
List<HBoxCell> list = new ArrayList<>();
for (int i = 0; i < 12; i++) {
list.add(new HBoxCell("Item " + i, "Button " + i));
}
ListView<HBoxCell> listView = new ListView<HBoxCell>();
ObservableList<HBoxCell> myObservableList = FXCollections.observableList(list);
listView.setItems(myObservableList);
layout.setCenter(listView);
return layout;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(200);
stage.show();
}
public static void main(String args[]) {
launch(args);
}
}
Wynik tej implementacji będzie wyglądał następująco:
Jednak, aby skutecznie zarządzać zdarzeniami Przycisków, należy rozważyć dodanie dodatkowego niestandardowego obiektu Button do metody hboxcustom constructor i stworzyć odpowiednią metodę, która pozwoli na sterowanie przyciski click-zdarzenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-11 10:21:11
Poprzednie rozwiązanie działało dobrze, gdy moja lista nie była duża i nie dodałem więcej kontrolek, czyniąc węzeł bardziej złożonym. Kiedy dodałem więcej kontrolek znalazłem problem z współbieżnością, może to również dlatego, że rysuję mapy, które mają więcej niż 12000 obiektów (linii i wielokątów). Problem (co widziałem) polegał na tym, że niektóre elementy w ListView będą powtarzane wewnętrznie, co oznacza, że cellfactory zostanie utworzony dwa razy i / lub nie. Wygląda na to, że "updateItem" jest ostatni w kolejce wątku. Aby obejść problem, muszę utworzyć węzeł przed utworzeniem ListView. Coś takiego:
ObservableList<Node> list = FXCollections.observableArrayList();
for (AisaLayer layer : listLayers) {
mapLayers.put(layer.getLayerName(), layer);
list.add(new AisaNode(layer));
System.out.println("Ordered:" + layer.getLayerName());
}
lv.setItems(list);
lv.setCellFactory(new Callback<ListView<Node>, ListCell<Node>>() {
@Override
public ListCell<Node> call(ListView<Node> param) {
return new DefaultListCell<>();
}
});
Użyłem prostego DefaultListCell zalecanego w fxexperience.com
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-31 17:26:23