【问题标题】:JavaFX handlers not triggered未触发 JavaFX 处理程序
【发布时间】:2014-05-09 11:05:46
【问题描述】:

我已经编写了以下代码。

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 事件处理程序真正被触发。其他的完全被忽略。可能是什么问题?

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

【问题讨论】:

  • 你能用工作示例更新你的问题吗?
  • 刚刚更新了帖子。现在它应该运行了。

标签: java javafx


【解决方案1】:

这是 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() 以查看当前由单元格表示的项目。

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

【讨论】:

  • 感谢您的帖子,现在可以使用了。感谢您所做的比我要求的还要多;)
【解决方案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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-15
    • 2013-02-22
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 1970-01-01
    相关资源
    最近更新 更多