将 setOnAction 方法添加到自定义场景生成器控件

Add setOnAction method to custom Scene Builder Control

所以我特别按照 this blog (part 2 创建了一个自定义控件并将其导入到 Scene Builder 中。

这是它的样子:

这是 fxml 文件:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<fx:root maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="30.0" prefWidth="150.0" type="AnchorPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <HBox alignment="CENTER" layoutX="-65.0" layoutY="-56.0" spacing="5.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <children>
            <Button fx:id="previousButton" minWidth="25.0" mnemonicParsing="false" prefWidth="25.0" text="&lt;" />
            <Label fx:id="label" alignment="CENTER" contentDisplay="CENTER" prefWidth="500.0" text="Label" />
            <Button fx:id="nextButton" minWidth="25.0" mnemonicParsing="false" prefWidth="25.0" text="&gt;" />
         </children>
      </HBox>
   </children>
</fx:root>

这是 class 文件:

package swipeselector;

import java.io.IOException;
import java.util.ArrayList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;

/**
 *
 * @author patrickb
 */
public class SwipeSelector extends AnchorPane {

    @FXML
    private Button previousButton;
    @FXML
    private Label label;
    @FXML
    private Button nextButton;

    ArrayList<String> items;
    int selectedIndex;
    private boolean repetitive;

    public SwipeSelector() {
        FXMLLoader fxmlLoader = new FXMLLoader(
                getClass().getResource("swipeSelectorFXML.fxml"));

        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }

        items = new ArrayList<>();
        selectedIndex = 0;

        previousButton.setOnAction((ActionEvent event) -> {
            setPrevious();
        });

        nextButton.setOnAction((ActionEvent event) -> {
            setNext();
        });

    }

    public void setItems(ArrayList<String> items) {
        this.items = items;
        selectFirst();
    }

    public void setPrevious() {
        updateItem(selectedIndex - 1);
    }

    public void setNext() {
        updateItem(selectedIndex + 1);
    }

    public void selectFirst() {
        updateItem(0);
    }

    private void selectLast() {
        updateItem(items.size() - 1);
    }

    private void updateItem() {
        if (items.isEmpty()) {
            label.setText("");
        } else {
            if (selectedIndex < 0) {
                if (repetitive) {
                    selectedIndex = items.size() - 1;
                } else {
                    selectedIndex = 0;
                }
            }
            if (selectedIndex >= items.size()) {
                if (repetitive) {
                    selectedIndex = 0;
                } else {
                    selectedIndex = items.size() - 1;
                }
            }
            label.setText(items.get(selectedIndex));
        }
    }

    private void updateItem(int selectedIndex) {
        this.selectedIndex = selectedIndex;
        updateItem();
    }

    public void setRepetitive(boolean cyclesThrough) {
        this.repetitive = cyclesThrough;
    }

}

一切正常,但是:当我单击下一个或上一个按钮时,我希望在我的原始项目中发生一些事情。我通常会通过向对象添加 setOnAction(new EventHandler ... 方法来做到这一点。虽然这个方法不存在,所以我需要以某种方式将它添加到我的自定义控件中。 如何让我的自定义控件在每次单击两个按钮之一时调用 ActionEvent,以及如何为我的对象创建一个有效的 setOnAction 方法?

您需要创建一个自定义事件。不幸的是,关于这方面的信息很少。您可以查看 ButtonBase 的源代码以获取示例。

您需要定义一个 onAction 属性:

ObjectProperty<EventHandler<ActionEvent>> onAction

我为此实现了一个实用程序 class:SimpleEventHandlerProperty

您可以在此处找到使用 SimpleEventHandlerProperty 的完整示例:LeftTestPane.java

您可以从 Maven Central 获取库:

<dependency>
    <groupId>org.drombler.commons</groupId>
    <artifactId>drombler-commons-fx-core</artifactId>
    <version>0.6</version>
</dependency>

我找到了让它工作的方法。下面的代码添加到Class文件中(基本上是从ButtonBase复制过来的。

public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { return onAction; }
    public final void setOnAction(EventHandler<ActionEvent> value) { onActionProperty().set(value); }
    public final EventHandler<ActionEvent> getOnAction() { return onActionProperty().get(); }
    private ObjectProperty<EventHandler<ActionEvent>> onAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {
        @Override protected void invalidated() {
            setEventHandler(ActionEvent.ACTION, get());
        }

        @Override
        public Object getBean() {
            return SwipeSelector.this;
        }

        @Override
        public String getName() {
            return "onAction";
        }
    };

此外,无论何时发生您希望触发该动作事件的事情,调用方法 fireEvent(new ActionEvent()); 都会为您执行此操作。此方法返回到节点 Class,它继承到此 class,因为它扩展了 AnchorPane(它从节点继承了 fireEvent 方法)。

所以在我的例子中,我用这个替换了我拥有的两个按钮的两个 onAction 方法:

previousButton.setOnAction((ActionEvent event) -> {
            setPrevious();
            fireEvent(event);
        });

nextButton.setOnAction((ActionEvent event) -> {
            setNext();
            fireEvent(event);
        });

现在,无论何时按下两个按钮中的一个,都会触发一个 ActionEvent(为此我刚刚传递了 Buttons 事件),我可以通过添加

在我的项目中捕获它
swipeSelector.setOnAction((ActionEvent event) -> {
            System.out.println("Event fired!!!");
            //DO SOMETHING 
        });

像往常一样。