想象一下定义鼠标模式的枚举:
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(可能是按键),则切换组会相应地进行调整。
我可以用2个听众做到这一点,但我想知道是否有办法创建自定义双向地图。
答案 0 :(得分:8)
我认为没有办法直接这样做。虽然是通用的
Bindings.bindBidirectional(Property<S> property1, Property<T> property2, Function<S,T> mapping, Function<T,S> inverseMapping)
可能是API的一个很好的补充,即使在这种情况下也没有用,因为ToggleGroup
的{{1}}是只读的(因为选择需要在每个{{1调用了selectedProperty
方法,以及Toggle
的{{1}})。
在这种情况下,使用几个听众就可以了。
与“自定义双向地图”最接近的是
setSelected(...)
方法。如果您有(可写)ToggleGroup
和(可写)selectedProperty
,理论上您可以使用两个双向绑定和一个中间Bindings.bindBiDirectional(StringProperty stringProperty, ObjectProperty<T> otherProperty, StringConverter<T> converter)
将它们绑定在一起。在实践中,这几乎总是比使用两个监听器更多的代码,而且效率也较低。
答案 1 :(得分:3)
我已成功使用JFXtras项目中的ToggleGroupValue类。它只在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);
}
}
答案 2 :(得分:0)
我使用的是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);
你需要为ToggleGroup设置一个小的BindingUtils类。
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());
});
}
答案 3 :(得分:0)
这个答案的灵感来自tunabot。这个答案将使用 RadioButton
而不是使用 ToogleButton
,为了使它看起来更漂亮,我们将使用 ControlsFX 中的 SegmentedButton
。我们可以使用 valueProperty
中的 ToggleGroupValue
绑定双向选中的切换按钮。
有一个调试按钮,当我们点击这个按钮时,选中的按钮会变成 DELETING
按钮。
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import jfxtras.scene.control.ToggleGroupValue;
import org.controlsfx.control.SegmentedButton;
public class ToggleBindingDemo extends Application{
public static void main(String[] args){
launch(args);
}
private final ObjectProperty<MouseMode> mouseModeObjectProperty = new SimpleObjectProperty<>(MouseMode.SELECTION);;
@Override
public void start(Stage stage){
ToggleGroupValue<MouseMode> toggleGroupValue = new ToggleGroupValue<>();
ToggleButton selection = new ToggleButton("Selection");
selection.setUserData(MouseMode.SELECTION);
selection.setToggleGroup(toggleGroupValue);
ToggleButton editing = new ToggleButton("Editing");
editing.setUserData(MouseMode.EDITING);
editing.setToggleGroup(toggleGroupValue);
ToggleButton deleting = new ToggleButton("Deleting");
deleting.setUserData(MouseMode.DELETING);
deleting.setToggleGroup(toggleGroupValue);
toggleGroupValue.valueProperty().bindBidirectional(mouseModeObjectProperty);
mouseModeObjectProperty.addListener(new ChangeListener<MouseMode>(){
@Override
public void changed(ObservableValue<? extends MouseMode> observable, MouseMode oldValue, MouseMode newValue){
System.out.println("MouseMode: " + newValue);
}
});
SegmentedButton segmentedButton = new SegmentedButton(selection, editing, deleting);
segmentedButton.setToggleGroup(toggleGroupValue);
Button debugButton = new Button("Debug");
debugButton.setOnMouseClicked(event -> handleDebugClick());
VBox vBox = new VBox(segmentedButton, debugButton);
vBox.setSpacing(10);
StackPane root = new StackPane(vBox);
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.show();
}
void handleDebugClick(){
mouseModeObjectProperty.set(MouseMode.DELETING);
}
public enum MouseMode{
SELECTION,
EDITING,
DELETING
}
}