JavaFX处理程序未触发

时间:2014-05-09 11:05:47

标签: java javafx

我写了以下代码。

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Pos;
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.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.util.Callback;

public class App extends Application {

    private ListView<String> listView;

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

    @Override
    public void start(Stage stage) throws Exception {
        List<String> friendList = new ArrayList<String>();
        friendList.add("Alice");
        friendList.add("Bob");

        listView = new ListView<>(FXCollections.observableArrayList(friendList));
        listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
            @Override
            public ListCell<String> call(ListView<String> p) {
                ListCell<String> cell = new ListCell<String>() {
                    @Override
                    protected void updateItem(String t, boolean empty) {
                        super.updateItem(t, empty);
                        if (t != null) {
                            Label usernameLabel = new Label(t);
                            usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 12));

                            Button callButton = new Button("Call");
                            callButton.setOnAction(e -> System.out.println("action")); // not working
                            callButton.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> System.out.println("entered"));
                            callButton.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("clicked")); // not working

                            HBox usernameBox = new HBox(5);
                            usernameBox.setAlignment(Pos.CENTER_LEFT);
                            usernameBox.getChildren().addAll(usernameLabel);

                            BorderPane borderPane = new BorderPane();
                            borderPane.setLeft(usernameBox);
                            borderPane.setRight(callButton);

                            VBox vbox = new VBox(3);
                            vbox.getChildren().addAll(borderPane);
                            setGraphic(vbox);
                        }
                    }
                };
                return cell;
            }
        });
        stage.setScene(new Scene(listView));
        stage.show();
    }
}

如果你看一下callButton,你会发现它有三个不同的处理程序。但是,实际上只触发了MOUSE_ENTERED事件处理程序。其他的完全被忽略了。可能是什么问题?

编辑:添加并删除了一些代码,以使其可以运行。

2 个答案:

答案 0 :(得分:3)

这是JavaFX 8中的known bug,已在最新的ea版本(1.8.0_20)中修复。

作为一种解决方法,创建一次控件并使用它们注册处理程序,然后只需在updateItem(...)方法中更新它们的状态:

    listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
        @Override
        public ListCell<String> call(ListView<String> p) {
            Label usernameLabel = new Label();
            usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 12));
            Button callButton = new Button("Call");

            HBox usernameBox = new HBox(5);
            usernameBox.setAlignment(Pos.CENTER_LEFT);
            usernameBox.getChildren().addAll(usernameLabel);

            BorderPane borderPane = new BorderPane();
            borderPane.setLeft(usernameBox);
            borderPane.setRight(callButton);

            VBox vbox = new VBox(3);
            vbox.getChildren().addAll(borderPane);

            ListCell<String> cell = new ListCell<String>() {

                @Override
                protected void updateItem(String t, boolean empty) {
                    super.updateItem(t, empty);
                    if (t != null) {
                        usernameLabel.setText(t);
                        setGraphic(vbox);
                    } else {
                        setGraphic(null); // you will have weird bugs without this: don't omit it
                    }
                }
            };

            callButton.setOnAction(e -> System.out.println("action: "+cell.getItem())); 
            callButton.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> System.out.println("entered "+ cell.getItem()));
            callButton.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("clicked "+ cell.getItem())); 

            return cell;
        }
    });

请注意,这种“解决方法”无论如何都是首选方法,也是“虚拟化”控件设计者所期望的方法,如ListViewTableView等。重点是应用程序可能经常调用updateItem(...),而很少创建单元格。通过在updateItem(...)方法中创建新控件,可能会引入性能问题。为单元格创建一次,然后在updateItem(...)中配置它们。另请注意我如何只注册事件处理程序一次,并让处理程序引用cell.getItem()以查看单元格当前表示的项目。

最后一件事:你的代码中有一个错误(我修复过)。由于单元格可以重复使用,包括显示项目的单元格被重用于空单元格的情况,因此始终处理项目为空的情况(通常通过将文本和/或图形设置为null)非常重要。 / p>

答案 1 :(得分:2)

您可以添加getIconAndResizeTo16( String s )的代码吗?我猜你返回的节点会消耗鼠标点击次数。

这是一个演示该问题的可运行示例。这只是猜测。

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class App extends Application {
    public static void main(String[] args) { launch(args); }

    @Override
    public void start(Stage stage) throws Exception {

        Button callButton = new Button("", getIconAndResizeTo16("Phone"));
        callButton.setOnAction(e -> System.out.println("clicked1")); // not working
        callButton.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> System.out.println("entered"));
        callButton.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("clicked"));  // not working

        Button chatButton = new Button("", getIconAndResizeTo16("Chat") );
        chatButton.setOnAction(e -> System.out.println("clicked2")); // not working

        HBox callIconBox = new HBox(3);
        callIconBox.setAlignment(Pos.CENTER_RIGHT);
        callIconBox.getChildren().addAll(callButton, chatButton);

        stage.setScene(new Scene(callIconBox));
        stage.show();
    }

    private Node getIconAndResizeTo16(String s) {
        Label l = new Label("Consumes " + s + " Events");
        l.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> { e.consume(); });
        l.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> { e.consume(); });
        return l;
    }
}