具有自定义单元格单击处理的ListView

时间:2017-10-27 17:12:17

标签: java listview javafx event-handling

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

我的组件如下所示:

component with custom cells

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

2 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:0)

你可以尝试类似的东西:

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