【问题标题】:JavaFX - ListView Item with an Image ButtonJavaFX - 带有图像按钮的 ListView 项
【发布时间】:2013-03-17 16:34:18
【问题描述】:

我想知道是否可以在每个 ListView 项之后添加一个图像按钮。例如:

那些红色方块应该有一个按钮。如果可以的话,如何处理item的按钮的点击事件?

编辑:如果有其他控件可以做到这一点,请告诉我。我正在测试 TableView。

提前致谢!

【问题讨论】:

    标签: listview javafx


    【解决方案1】:

    我最近正在测试这个。我的解决方案:

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

    单元格将如下所示:

    相关部分是XCell.updateItem 方法和setGraphic 调用。对于setGraphic(),通常应为标签设置图标,但设置复杂节点也同样适用——在本例中为带有标签和按钮的 HBox。

    您需要确保 Button 的事件处理程序引用列表中的正确项目。在下面的第一个链接中,提到了当前选择的项目现在可能就足够了。所以在处理按钮事件的时候获取列表中当前选中的索引。

    你可能想看看这些:

    【讨论】:

    • 太棒了!非常感谢编码!工作得很好!
    • 无需将当前项目存储在lastItem 字段中。您可以直接在按钮处理程序中调用getItem()
    【解决方案2】:

    您还可以使用自定义 HBox 对象列表。下面的例子展示了这种自定义 HBox 内部类的使用以及将这些对象的列表嵌套到 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);
         }
    }
    

    此实现的结果将如下所示:

    但是,为了有效地管理按钮事件,您应该考虑在 HBoxCustom 构造方法中添加额外的自定义按钮对象,并创建一个适当的方法,该方法将允许您控制按钮的点击事件。

    【讨论】:

    • 它不适用于 @FXML 初始化的 ListViews :(
    • @CarlosLópezMarí 这确实适用于 FXML 初始化的 ListViews
    • 这个我没有深入研究,但从表面上看,这似乎是一个糟糕的想法。
    【解决方案3】:

    当我的列表不大并且我没有添加更多控件使节点更复杂时,以前的解决方案运行良好。当我添加更多控件时,我发现并发问题,可能也是因为我正在绘制具有超过 12000 个对象(线和多边形)的地图。问题(我可以看到)是 ListView 中的某些项目将在内部重复,这意味着 cellfactory 将被创建两次和/或不被创建。似乎“updateItem”是线程队列中的最后一个。 所以要解决这个问题,我必须在创建 ListView 之前创建节点。像这样的:

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

    我使用了 fxexperience.com 推荐的简单 DefaultListCell

    【讨论】:

      猜你喜欢
      • 2017-03-20
      • 1970-01-01
      • 2015-07-17
      • 1970-01-01
      • 2016-11-20
      • 1970-01-01
      • 2017-09-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多