Привязка ToggleGroup двунаправленно в javafx

представьте себе, что перечисление, определяющее режимы мыши:

public enum MouseMode {
SELECTION,
EDITING,
DELETING }

и представьте, что у вас есть тумблер-группа из 3 кнопок:

    ToggleButton selection = new ToggleButton("Select");
    ToggleButton editing = new ToggleButton("Edit");
    ToggleButton deleting = new ToggleButton("Delete");
    ToggleGroup mouseSelection = new ToggleGroup();

Я хочу, чтобы поле MouseMode currentMode для двунаправленной связи с переключаемой группой. Всякий раз, когда переключатель установлен, currentMode переключается соответственно, но и если какой-то внешний процесс изменяет currentMode (возможно, нажатие клавиши), то togglegroup адаптируется соответственно.

Я могу сделать это с 2 слушателями, но мне интересно, есть ли способ создайте пользовательскую двунаправленную карту.

3 ответов


Я не думаю, что есть способ сделать это напрямую. Хотя общего назначения

Bindings.bindBidirectional(Property<S> property1, Property<T> property2, Function<S,T> mapping, Function<T,S> inverseMapping)

может стать хорошим дополнением к API, даже это не поможет в этом случае, как ToggleGroup ' s selectedProperty только для чтения (так как выбор должен обрабатываться, когда каждый Toggle ' s setSelected(...) вызывается метод, а также с помощью ToggleGroup ' s selectedProperty).

использование нескольких слушателей-это путь в этом случае.

самое близкое к " custom двунаправленная карта " - это

Bindings.bindBiDirectional(StringProperty stringProperty, ObjectProperty<T> otherProperty, StringConverter<T> converter)

метод. В случае, когда у вас есть (записываемый) ObjectProperty<S> и (для записи) ObjectProperty<T> теоретически вы можете использовать две двунаправленные привязки и промежуточное StringProperty чтобы связать их вместе. На практике это почти всегда больше кода, чем просто использование двух слушателей, а также менее эффективно.


Я успешно использовал класс ToggleGroupValue в проекте JFXtras. Это только в 2.2 кода, а не 8.0, но его достаточно легко скопировать в свой код. https://github.com/JFXtras/jfxtras-labs/blob/2.2/src/main/java/jfxtras/labs/scene/control/ToggleGroupValue.java

вот пример:

import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class Main extends Application {
    Child myChild = new Child();
    @Override
    public void start( Stage stage ) throws Exception {
        stage.setTitle( "ToggleGroupValue Example" );
        GridPane gridPane = new GridPane();
        int rowIndex = 0;
        gridPane.add( new Label("Nickname: "), 0, rowIndex );

        ToggleGroupValue toggleGroupValue = new ToggleGroupValue();
        rowIndex = createAddRadioButtons( gridPane, rowIndex, toggleGroupValue );

        gridPane.add( new Label("Selected Nickname: "), 0, rowIndex );
        Label selectedNickNameValueLabel = new Label();
        gridPane.add( selectedNickNameValueLabel, 1, rowIndex );

        myChild.nicknameProperty().bindBidirectional( toggleGroupValue.valueProperty() );
        selectedNickNameValueLabel.textProperty().bind( toggleGroupValue.valueProperty() );

        stage.setScene( new Scene( gridPane, 300, 100 ) );
        stage.show();
    }

    private int createAddRadioButtons( GridPane gridPane, int rowIndex, ToggleGroupValue toggleGroupValue ) {
        RadioButton radioButtonPunkin = new RadioButton();
        radioButtonPunkin.setUserData( "Punkin" );
        RadioButton radioButtonLittleBoy = new RadioButton();
        radioButtonLittleBoy.setUserData( "Little Boy" );
        RadioButton radioButtonBuddy = new RadioButton();
        radioButtonBuddy.setUserData( "Buddy" );
        List<RadioButton> radioButtons = Arrays.asList( radioButtonPunkin, radioButtonLittleBoy, radioButtonBuddy );
        for ( RadioButton radioButton : radioButtons ) {
            toggleGroupValue.add( radioButton, radioButton.getUserData() );
            radioButton.setText( radioButton.getUserData().toString() );
            gridPane.add( radioButton, 1, rowIndex++ );
        }
        return rowIndex;
    }

    private static class Child {
        private StringProperty nickname = new SimpleStringProperty();
        public StringProperty nicknameProperty() {
            return nickname;
        }
        public String getNickname() {
            return nickname.get();
        }
        public void setNickname( String notesProperty ) {
            this.nickname.set( notesProperty );
        }
    }

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

screenshot of javafx example application


Я использую адаптер свойств Java bean, но вы можете просто использовать последнюю строку этого кода и связать его.

JavaBeanObjectProperty<fooEnum> property = null;
    try {
        property = new JavaBeanObjectPropertyBuilder<fooEnum>().bean(fooBean).name(fooField).build();
    } catch (NoSuchMethodException e1) {
        e1.printStackTrace();
    }
    property.addListener((obs, oldValue, newValue) -> {
        System.out.println("Property value changed from " + oldValue + " to " + newValue);
    });
BindingUtils.bindToggleGroupToProperty(fooToggleGroup, property);

вам нужно иметь небольшой класс BindingUtils для ToggleGroup.

public final class BindingUtils {

private BindingUtils() {
}

public static <T> void bindToggleGroupToProperty(final ToggleGroup toggleGroup, final ObjectProperty<T> property) {
    // Check all toggles for required user data
    toggleGroup.getToggles().forEach(toggle -> {
        if (toggle.getUserData() == null) {
            throw new IllegalArgumentException("The ToggleGroup contains at least one Toggle without user data!");
        }
    });
    // Select initial toggle for current property state
    for (Toggle toggle : toggleGroup.getToggles()) {
        if (property.getValue() != null && property.getValue().equals(toggle.getUserData())) {
            toggleGroup.selectToggle(toggle);
            break;
        }
    }
    // Update property value on toggle selection changes
    toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
        property.setValue((T) newValue.getUserData());
    });
}