Пользовательский элемент управления JavaFX не отображает параметр «При действии»

Привет всем, мне нужна помощь со следующим:

Я пытаюсь добавить «При действии» к своему пользовательскому элементу управления, который я создаю в Scene Builder 2.0.

В моей сцене будет несколько таких, поэтому я хочу иметь только один обработчик для всех этих кнопок переключения. Проблема в том, что у моего пользовательского элемента управления нет раздела «При действии» в разделе «Код:», как у других элементов управления?

Большинство встроенных элементов управления в разделе «Код:» выглядят следующим образом:

Как добавить эту функцию в свой пользовательский элемент управления?

Мой код кнопки переключения:

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 SliderSwitch.this;
        }

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

Загружая его в Scene Builder 2.0, я все еще не вижу никаких опций действия на вкладке «Код».

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Пользовательские компоненты не получают автоматически свойство «при действии». Вам действительно нужно реализовать свойство onAction в коде. В качестве примера взгляните на реализации встроенных элементов управления, которые предоставляют такое свойство. Обычно реализация свойства выглядит примерно так:

// assumes 'this' is some subtype of 'javafx.scene.Node'
private final ObjectProperty<EventHandler<ActionEvent>> onAction =
    new SimpleObjectProperty<>(this, "onAction") {
      @Override
      protected void invalidated() {
        setEventHandler(ActionEvent.ACTION, get());
      }  
    };
public final void setOnAction(EventHandler<ActionEvent> onAction) { this.onAction.set(onAction); }
public final EventHandler<ActionEvent> getOnAction() { return onAction.get(); }
public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { return onAction; }

Но учтите, этого недостаточно. Пользовательский компонент также должен активировать ActionEvent всякий раз, когда это необходимо. Когда это уместно? Ну, это зависит от пользовательского компонента.

И, наконец, Scene Builder, к сожалению, не помещает свойство onAction пользовательского компонента в аккордеон «Код». Он расположен в аккордеоне «Свойства» в разделе «Пользовательский» вверху (см. снимок экрана в конце примера ниже). Я не знаю, как это изменить.

Пара примечаний:


Пример

Вот пример пользовательского элемента управления «переключатель», который предоставляет свойство onAction. В этом примере пользовательский элемент управления фактически расширяется Control, что означает, что есть также класс «скин» и класс «поведение», чтобы разделить вещи.

В конце ответа есть скриншот Scene Builder.

Исходный код

Скомпилировано и протестировано с использованием Java 22.0.2 и JavaFX 22.0.2.

Switch.java

package com.example.control;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.PseudoClass;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;

public class Switch extends Control {

  public Switch() {
    getStyleClass().add(DEFAULT_STYLE_CLASS);
  }

  public Switch(boolean selected) {
    this();
    setSelected(selected);
  }

  public void toggle() {
    if (!isDisabled() && !selected.isBound()) {
      setSelected(!isSelected());
    }
  }

  @Override
  protected Skin<?> createDefaultSkin() {
    return new SwitchSkin(this);
  }

  /* **************************************************************************
   *                                                                          *
   * Properties                                                               *
   *                                                                          *
   ****************************************************************************/

  // -- selected property

  private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected") {

    private boolean wasSelected;

    @Override
    protected void invalidated() {
      boolean isSelected = get();
      if (wasSelected != isSelected) {
        pseudoClassStateChanged(SELECTED, isSelected);
        fireEvent(new ActionEvent());
        wasSelected = isSelected;
      }
    }
  };

  public final void setSelected(boolean selected) {
    this.selected.set(selected);
  }

  public final boolean isSelected() {
    return selected.get();
  }

  public final BooleanProperty selectedProperty() {
    return selected;
  }

  // -- onAction property

  private ObjectProperty<EventHandler<? super ActionEvent>> onAction;

  public final void setOnAction(EventHandler<? super ActionEvent> onAction) {
    if (this.onAction != null || onAction != null) {
      onActionProperty().set(onAction);
    }
  }

  public final EventHandler<? super ActionEvent> getOnAction() {
    return onAction == null ? null : onAction.get();
  }

  public final ObjectProperty<EventHandler<? super ActionEvent>> onActionProperty() {
    if (onAction == null) {
      onAction = new SimpleObjectProperty<>(this, "onAction") {
        @Override
        protected void invalidated() {
          setEventHandler(ActionEvent.ACTION, get());
        }
      };
    }
    return onAction;
  }

  /* **************************************************************************
   *                                                                          *
   * CSS                                                                      *
   *                                                                          *
   ****************************************************************************/

  private static final String DEFAULT_STYLE_CLASS = "switch";
  private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
}

SwitchSkin.java

package com.example.control;

import javafx.animation.Animation;
import javafx.animation.FillTransition;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;

class SwitchSkin extends SkinBase<Switch> {

  private static final Duration ANIMATION_DURATION = Duration.millis(100);

  private final Circle thumb = new Circle(10);

  private final ParallelTransition animation;
  private final TranslateTransition translateAnimation;

  private SwitchBehavior behavior;

  SwitchSkin(Switch control) {
    super(control);

    var fillAnimation = new FillTransition(ANIMATION_DURATION);
    fillAnimation.setFromValue(Color.FIREBRICK);
    fillAnimation.setToValue(Color.FORESTGREEN);
    thumb.setFill(fillAnimation.getFromValue());

    translateAnimation = new TranslateTransition(ANIMATION_DURATION);
    translateAnimation.setFromX(0);

    animation = new ParallelTransition(thumb, fillAnimation, translateAnimation);
  }

  @Override
  public void install() {
    var control = getSkinnable();

    var bgFill = new BackgroundFill(Color.GRAY, new CornerRadii(10), new Insets(2));
    control.setBackground(new Background(bgFill));

    control.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
    control.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
    getChildren().add(thumb);

    registerChangeListener(control.selectedProperty(), _ -> selectedChanged());

    behavior = new SwitchBehavior(control);
  }

  @Override
  public void dispose() {
    super.dispose();
    if (behavior != null) {
      behavior.dispose();
      behavior = null;
    }
  }

  private void selectedChanged() {
    animation.setRate(isSelected() ? 1 : -1);
    animation.play();
  }

  private boolean isSelected() {
    return getSkinnable().isSelected();
  }

  private boolean animationNotRunning() {
    return animation.getStatus() != Animation.Status.RUNNING;
  }

  @Override
  protected void layoutChildren(
      double contentX, double contentY, double contentWidth, double contentHeight) {
    positionInArea(
        thumb, contentX, contentY, contentWidth, contentHeight, -1, HPos.LEFT, VPos.CENTER);

    double toX = contentX + contentWidth - thumb.getLayoutBounds().getWidth();
    translateAnimation.setToX(toX);
    if (isSelected() && animationNotRunning() && thumb.getTranslateX() != toX) {
      animation.setRate(1);
      animation.playFromStart();
    } else if (!isSelected() && animationNotRunning() && thumb.getTranslateX() != 0) {
      animation.setRate(-1);
      animation.playFrom(ANIMATION_DURATION);
    }
  }

  @Override
  protected double computePrefWidth(
      double height, double topInset, double rightInset, double bottomInset, double leftInset) {
    return leftInset + rightInset + (thumb.getRadius() * 4);
  }

  @Override
  protected double computePrefHeight(
      double width, double topInset, double rightInset, double bottomInset, double leftInset) {
    return topInset + bottomInset + (thumb.getRadius() * 2);
  }
}

SwitchBehavior.java

package com.example.control;

import java.util.Objects;
import javafx.event.EventHandler;
import javafx.event.WeakEventHandler;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;

class SwitchBehavior {

  private final EventHandler<MouseEvent> onClick = this::handleMouseClicked;
  private final WeakEventHandler<MouseEvent> weakOnClick = new WeakEventHandler<>(onClick);

  private final Switch node;

  SwitchBehavior(Switch node) {
    this.node = Objects.requireNonNull(node);
    node.addEventHandler(MouseEvent.MOUSE_CLICKED, weakOnClick);
  }

  private void handleMouseClicked(MouseEvent event) {
    if (event.getButton() == MouseButton.PRIMARY) {
      node.toggle();
    }
  }

  void dispose() {
    node.removeEventHandler(MouseEvent.MOUSE_CLICKED, weakOnClick);
  }
}

Конструктор сцен

Использование Конструктора сцен 22.0.0.

Ах, я вижу проблему. В версии 2.0 он не отображается, но я вижу, что он отображается в версии 22.

StealthRT 13.08.2024 02:03

Другие вопросы по теме

Похожие вопросы

Как интерполировать цвет узла с помощью пользовательских переменных CSS?
Try — catch не может перехватить исключение, потому что, когда исключение произошло, введите рекурсию в catch, блок try не работает и просто попадает в блок catch
Каковы распространенные методы поиска второго по величине элемента массива и какова временная сложность каждого метода?
Откуда мы можем получить com.sun.mail?
Как заставить этот метод всегда возвращать неотрицательное значение?
Значение слов «in» и «out» в дженериках Kotlin
Попытка перехватить исключения при преобразовании массива строк в целые числа
Столкновение с NullPointerException при попытке внедрить репозиторий в приложение весенней загрузки
Почему этот пример сложения чисел с плавающей запятой в Java ведет себя так, как будто мантисса имеет длину 24 бита?
ThreadPoolExecutor плохо масштабируется между 6->62 потоками