【问题标题】:ListView with custom Cell click handling具有自定义单元格点击处理的 ListView
【发布时间】:2018-04-09 08:52:23
【问题描述】:

在我的 JavaFX 应用程序中,我将 ListView 与自定义单元格一起使用。我想处理列表项点击的方式与点击项目下方的空白区域不同。我在整个 ListView 上设置了一个事件侦听器,但我无法确定单击了哪些项目(getSelectedItem()null,可能是因为我的自定义单元格代码中的错误)。以下情况如何正确处理?

我的组件如下所示:

<fx:root type="javafx.scene.layout.VBox">
    <Label fx:id="dayname" text="${controller.day.name}" />
    <ListView fx:id="appointmentsListView" items="${controller.day.events}" 
        onMouseClicked="#handleAppointmentsClick" />
</fx:root>

ListView 具有在组件构造函数中设置的自定义单元工厂:

public class DayComponent extends VBox {
    @FXML
    private ListView<Appointment> appointmentsListView;

    public DayComponent() throws IOException {
        // ...
        appointmentsListView.setCellFactory(l -> new AppointmentCell());
    }

    @FXML
    public void handleAppointmentsClick(MouseEvent event) {
        System.out.println(appointmentsListView.getSelectionModel()
            .getSelectedItem()); // null in every case
    }
}

自定义单元格代码:

public class AppointmentCell extends ListCell<Appointment> {

    @Override
    protected void updateItem(Appointment item, boolean empty) {
        super.updateItem(item, empty);
        if (!empty) {
            setGraphic(new AppointmentLabel(item));
        } else {
            setGraphic(null);
        }
    }
}

【问题讨论】:

  • @James_D 我已经尝试过这种方法,但如果列表为空,这将不起作用。我也需要处理空白点击。
  • 你可以指定一个自定义占位符并注册一个监听器来处理这种情况
  • 这可能是一个解决方案,但我仍然希望找到更清洁的方法。
  • 或者,如果单元格非空,则只需在单元格上使用鼠标处理程序中的事件,然后在列表视图上注册另一个鼠标处理程序。仅当单元鼠标处理程序未使用该事件时,才应调用该处理程序。这可能会提供一种合理的方法来将“空”功能与“非空”功能分开。

标签: java listview javafx event-handling


【解决方案1】:

你可以试试这样的:

public class AppointmentCellMouseClickHandler implements EventHandler<MouseEvent> {

    private final Appointment appointment;

    public AppointmentCellMouseClickHandler(Appointment appointment) {
        this.appointment = appointment;
    }

    @Override
    public void handle(MouseEvent arg0) {
        //Do stuff with appointment
    }
}

public class EmptyAppointmentCellMouseClickHandler implements EventHandler<MouseEvent> {

    @Override
    public void handle(MouseEvent arg0) {
        //Do stuff without appointment
    }
}

public class AppointmentCell extends ListCell<Appointment> {

    @Override
    protected void updateItem(Appointment item, boolean empty) {
        super.updateItem(item, empty);
        if (!empty) {
            setGraphic(new AppointmentLabel(item));
            this.setOnMouseClicked(new AppointmentCellMouseClickHandler(item));
        } else {
            setGraphic(null);
            this.setOnMouseClicked(new EmptyAppointmentCellMouseClickHandler());
        }
    }
}

与评论相关的编辑:

要在 ListView 为空时进行处理,您可以这样做:

//To handle the initialisation where the listener won't be call
appointmentsListView.setOnMouseClicked(new EmptyAppointmentCellMouseClickHandler());
appointmentsListView.getItems().addListener(new ListChangeListener<Appointment>() {
            @Override
            public void onChanged(Change<? extends Appointment> change) {
                while(change.next()){
                    //Toogle the listener depending on the content of the list
                    listView.setOnMouseClicked(change.getList().isEmpty() ? new EmptyAppointmentCellMouseClickHandler() : null);
                }
            }
        });

【讨论】:

  • 如果列表为空,上述解决方案不起作用,但我仍然需要处理这种情况。
  • 有 2 个解决方案来处理这个问题,第一个包括在列表中添加一个占位符(使用 setPlaceholder(Node) 方法),如一个不可见的窗格,并在鼠标单击时设置一个侦听器对于那个占位符。此解决方案更易于实施,但您必须处理占位符的图形方面。第二个是在列表视图包含的项目列表上添加一个 ChangeListener,这将根据列表是否为空来调整列表视图上的侦听器。
【解决方案2】:

一种相当干净的方法是在列表视图中向单元格注册鼠标侦听器,以处理对非空单元格的点击。如果单元格非空,则使用鼠标事件。在列表视图本身上注册第二个鼠标侦听器,以处理对空单元格的单击(如果列表视图为空,则在列表视图的占位符上)。这需要两个处理程序,但至少以合理的方式将“空”和“非空”功能分开。

这是一个简单的例子:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class ListViewMouseHandlerExample extends Application {

    private int count = 0 ;

    @Override
    public void start(Stage primaryStage) {
        ListView<String> listView = new ListView<>();

        Button addButton = new Button("Add");
        addButton.setOnAction(e -> listView.getItems().add("Item " + (++count)));

        Button deleteButton = new Button("Delete");
        deleteButton.setOnAction(e -> listView.getItems().remove(listView.getSelectionModel().getSelectedIndex()));
        deleteButton.disableProperty().bind(listView.getSelectionModel().selectedItemProperty().isNull());

        listView.setCellFactory(lv -> {
            ListCell<String> cell = new ListCell<String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    setText(item);
                }
            };
            cell.setOnMouseClicked(e -> {
                if (!cell.isEmpty()) {
                    System.out.println("You clicked on " + cell.getItem());
                    e.consume();
                }
            });
            return cell;
        });

        listView.setOnMouseClicked(e -> {
            System.out.println("You clicked on an empty cell");
        });

        BorderPane root = new BorderPane(listView);
        ButtonBar buttons = new ButtonBar();
        buttons.getButtons().addAll(addButton, deleteButton);
        root.setBottom(buttons);


        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    • 1970-01-01
    • 2019-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多