【问题标题】:Using a StringProperty as a parameter of a KeyValue method使用 StringProperty 作为 KeyValue 方法的参数
【发布时间】:2016-06-14 21:29:28
【问题描述】:

我正在尝试通过使用 CSS 文件逐渐更改其样式来制作动画按钮。

我对此进行了研究,并得到了this这个问题。我看到了接受的答案,并尝试实施该人提供的解决方案。

我所做的基本上是创建一个扩展ButtonCustomButton 类,然后设置StringProperties,其值根据是否单击按钮而改变;然后,每个StringProperty 都被连接起来,这样它们就可以作为 CSS 文件的替代品,作为按钮的样式应用。

我认为一切编码都很好,问题是我无法创建按下按钮时应该播放的动画,因为为此我需要创建一个 TimelineKeyframe 的 @ 987654330@s 指定了每个符合动画的组件的值,结果StringProperties 不能作为KeyValue 方法的参数。

这是 CustomButton 类:

public class CustomButton extends Button{


BooleanProperty clicked = new SimpleBooleanProperty(false); // Its value changes each time the button is clicked.

StringProperty oldRotationValue = new SimpleStringProperty("-fx-rotate: -360;"); // Stores the last value of rotationStringProperty, so 
                                                                                 // then it can be used as the first frame of an animation.

StringProperty oldColorValue = new SimpleStringProperty("#E74C3C;"); // Stores the last value of colorStringProperty, so 
                                                                     // then it can be used as the first frame of an animation.

StringProperty oldSizeValue = new SimpleStringProperty("-fx-size: " + "180px;"); // Stores the last value of sizeStringProperty, so 
                                                                                 // then it can be used as the first frame of an animation.
public CustomButton(){
    Button button = createButton(rotationStringProperty, colorStringProperty, sizeStringProperty);
} //CustomButton constructor

StringProperty rotationStringProperty = new SimpleStringProperty(); // Creates the rotationStringProperty.
StringProperty colorStringProperty = new SimpleStringProperty(); // Creates the colorStringProperty.
StringProperty sizeStringProperty = new SimpleStringProperty(); // Creates the sizeStringProperty.

private void setRotationStringProperty() { // Method that sets the rotation value depending on whether the button was pressed or not.

    if (!clicked.getValue()) { // If the button wasn't clicked.
        oldRotationValue.set("-fx-rotate: " + "-360;");
        rotationStringProperty.set("-fx-rotate: " + "360;");
    } else { // If the button was clicked.
        oldRotationValue.set("-fx-rotate: " + "360;");
        rotationStringProperty.set("-fx-rotate: " + "-360;");
    }
}

private StringProperty setColorStringProperty() { // Method that sets the color depending on whether the button was pressed or not.
    if (!clicked.getValue()) { // If the button wasn't clicked.
        oldColorValue.set("#EA6153;");
        colorStringProperty.set("#E74C3C;");
    } else { // If the button was clicked.
        oldColorValue.set("#E74C3C;");
        colorStringProperty.set("#EA6153;");
    }
}

private StringProperty setSizeStringProperty() { // Method that sets the size depending on whether the button was pressed or not.
    if (!clicked.getValue()) { // If the button wasn't pressed
        oldSizeValue.set("-fx-size: " + "200px;");
        sizeStringProperty.set("-fx-size: " + "180px;");
    } else { // If the button was pressed.
        oldSizeValue.set("-fx-size: " + "180px;");
        sizeStringProperty.set("-fx-size: " + "200px;");
    }
}

private void setSizeStringMediumProperty(){ // Sets the size that the button must have in the middle of the animation.
    if(!clicked.getValue()){ // If the button wasn't pressed.
        sizeStringProperty.set("-fx-size: " + "170px;");
    }else{ // If the button was pressed.
        sizeStringProperty.set("-fx-size: " + "210px;"); 
    }
}

private Button createButton(StringProperty rotationStringProperty, //Method that creates a button and defines the its rotation, 
                                                                   // color, size, and how these has to be animated.
                                                                   // Once everything is done, it returns the customized button.
                            StringProperty colorStringProperty,
                            StringProperty sizeStringProperty) {

    Button button = new Button(); // Creates a normal JavaFX Button so then it can be modified.

    buttonSetOnAction(button); // Sets the action that the button must perform when it's clicked.

    setRotationStringProperty(); // Sets the rotationStringProperty.
    setColorStringProperty(); // Sets the colorStringProperty.
    setSizeStringProperty(); // Sets the sizeStringProperty.

    button.styleProperty().bind(new SimpleStringProperty("-fx-background-color: ") // Set's the style of the button.
                                .concat(colorStringProperty)
                                .concat(sizeStringProperty)
                                .concat(rotationStringProperty)
                                .concat("fx-border-color; #c0392b;")
                                .concat("-fx-border-width: 15px;"));
    return button; // Returns the button.
}

private void buttonSetOnAction(Button button){ // Definition of a method that sets the actions that the button must perform when it's clicked.
    button.setOnAction(e -> {
        clicked.set(!clicked.getValue()); // The value of clicked is set to its opposite.
        Timeline animation = new Timeline( //Defines an animation
                new KeyFrame(Duration.seconds(0), new KeyValue(oldRotationValue, oldColorValue, oldSizeValue));
                setSizeStringMediumProperty(); // Sets the size that the button must have at the middle of the animation.
                new KeyFrame(Duration.seconds(0.25), new KeyValue(sizeStringProperty));
                setSizeStringProperty(); // Sets the size that the button must have at the end of the animation.
                new KeyFrame(Duration.seconds(0.50), new KeyValue(rotationStringProperty, colorStringProperty, sizeStringProperty));
                );
        animation.play(); // Plays the animation;
    });
}
}

谁能想到使用 StringProperties 的替代方法?

注意:我知道"-fx-size: " 什么都不做,但我不知道如何在 CSS 中更改按钮的大小。一旦我能解决动画问题,我会在另一个问题中问这个问题。

更新:我刚刚通过实现@James_D 的替代方案编辑了代码:

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.control.Button;
import javafx.util.Duration;

public class CustomButton extends Button{


    BooleanProperty clicked = new SimpleBooleanProperty(false); // Its value changes each time the button is clicked.

    IntegerProperty oldRotationValue = new SimpleIntegerProperty(-360); // Stores the last value of rotationIntegerProperty, so 
                                                                        // then it can be used as the first frame of an animation.

    IntegerProperty oldColorValue = new SimpleIntegerProperty(Integer.decode("E74C3C")); // Stores the last value of colorIntegerProperty, so 
                                                                                           // then it can be used as the first frame of an animation.

    IntegerProperty oldSizeValue = new SimpleIntegerProperty(180); // Stores the last value of sizeIntegerProperty, so 
                                                                   // then it can be used as the first frame of an animation.
    public CustomButton(){ //CustomButton constructor
        Button button = createButton(rotationIntegerProperty, colorIntegerProperty, sizeIntegerProperty);

    }

    IntegerProperty rotationIntegerProperty = new SimpleIntegerProperty(); // Creates the rotationIntegerProperty.
    IntegerProperty colorIntegerProperty = new SimpleIntegerProperty(); // Creates the rotationIntegerProperty.
    IntegerProperty sizeIntegerProperty = new SimpleIntegerProperty(); // Creates the sizeIntegerProperty.

    private IntegerProperty setRotationIntegerProperty() { // Method that sets the rotation value depending on whether the button was pressed or not.

        IntegerProperty newRotationIntegerProperty = new SimpleIntegerProperty();

        if (!clicked.getValue()) { // If the button wasn't clicked.
            oldRotationValue.set(-360);
            rotationIntegerProperty.set(360);
        } else { // If the button was clicked.
            oldRotationValue.set(260);
            rotationIntegerProperty.set(-360);
        }

        return newRotationIntegerProperty;

    }

    private IntegerProperty setColorIntegerProperty() { // Method that sets the color depending on whether the button was pressed or not.

        IntegerProperty newColorIntegerProperty = new SimpleIntegerProperty();

        if (!clicked.getValue()) { // If the button wasn't clicked.
            oldColorValue.set(Integer.decode("EA6153")); // oldColorValue.set("#EA6153;");
            colorIntegerProperty.set(Integer.decode("E74C3C"));
        } else { // If the button was clicked.
            oldColorValue.set(Integer.decode("E74C3C"));
            colorIntegerProperty.set(Integer.decode("EA6153"));
        }

        return newColorIntegerProperty;

    }

    private IntegerProperty setSizeIntegerProperty() { // Method that sets the size depending on whether the button was pressed or not.

        IntegerProperty newSizeIntegerProperty = new SimpleIntegerProperty();

        if (!clicked.getValue()) { // If the button wasn't pressed
            oldSizeValue.set(200);
            sizeIntegerProperty.set(180);
        } else { // If the button was pressed.
            oldSizeValue.set(180);
            sizeIntegerProperty.set(200);
        }

        return newSizeIntegerProperty;

    }

    private IntegerProperty setSizeIntegerMediumProperty(){ // Sets the size that the button must have in the middle of the animation.

        IntegerProperty newSizeIntegerMediumProperty = new SimpleIntegerProperty();

        if(!clicked.getValue()){ // If the button wasn't pressed.
            sizeIntegerProperty.set(180);
        }else{ // If the button was pressed.
            sizeIntegerProperty.set(180); 
        }

        return newSizeIntegerMediumProperty;

    }

    private Button createButton(IntegerProperty rotationIntegerProperty, //Method that creates a button and defines the its rotation, 
                                                                         // color, size, and how these has to be animated.
                                                                         // Once everything is done, it returns the customized button.
                                IntegerProperty colorIntegerProperty,
                                IntegerProperty sizeIntegerProperty) {

        Button button = new Button(); // Creates a normal JavaFX Button so then it can be modified.

        buttonSetOnAction(button); // Sets the action that the button must perform when it's clicked.

        setRotationIntegerProperty(); // Sets the rotationIntegerProperty.
        setRotationIntegerProperty(); // Sets the colorIntegerProperty.
        setSizeIntegerProperty(); // Sets the sizeIntegerProperty.

        button.styleProperty().bind(Bindings.format( // Set's the style of the button.
                               "-fx-pref-width: #%f;"
                               + "-fx-pref-height: #%f;"
                               + "-fx-rotate: %f;"
                               + "-fx-background-color: #%s;"
                               + "-fx-border-color: #c0392b;"
                               + "-fx-border-width: 15px;",
                               sizeIntegerProperty, sizeIntegerProperty, rotationIntegerProperty, colorIntegerProperty));
        return button; // Returns the button.
    }

    private void buttonSetOnAction(Button button){ // Definition of a method that sets the actions that the button must perform when it's clicked.

        Timeline animation = new Timeline();

        button.setOnAction(e -> {

            clicked.set(!clicked.getValue()); // The value of clicked is set to its opposite.

            animation.getKeyFrames().clear();

                animation.getKeyFrames().addAll(new KeyFrame(Duration.seconds(0),
                        new KeyValue (rotationIntegerProperty, oldRotationValue.getValue()),
                        new KeyValue (colorIntegerProperty, oldColorValue.getValue()),
                        new KeyValue (sizeIntegerProperty, oldSizeValue.getValue())),
                                             new KeyFrame(Duration.seconds(0.25),
                        new KeyValue (sizeIntegerProperty, setSizeIntegerMediumProperty().getValue())),
                                             new KeyFrame(Duration.seconds(0.25),
                        new KeyValue (sizeIntegerProperty, setSizeIntegerProperty().getValue()),
                        new KeyValue (rotationIntegerProperty, setRotationIntegerProperty().getValue()),
                        new KeyValue (colorIntegerProperty, setColorIntegerProperty().getValue())));

                animation.play(); // Plays the animation;

                 button.disableProperty().bind(animation.statusProperty().isEqualTo(Animation.Status.RUNNING));


        });
    }
}

在我执行代码之前一切似乎都很好,控制台会打印出来:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.util.IllegalFormatConversionException: f != java.lang.Integer
    at java.util.Formatter$FormatSpecifier.failConversion(Unknown Source)
    at java.util.Formatter$FormatSpecifier.printFloat(Unknown Source)
    at java.util.Formatter$FormatSpecifier.print(Unknown Source)
    at java.util.Formatter.format(Unknown Source)
    at java.util.Formatter.format(Unknown Source)
    at java.lang.String.format(Unknown Source)
    at com.sun.javafx.binding.StringFormatter$4.computeValue(Unknown Source)
    at javafx.beans.binding.StringBinding.get(Unknown Source)
    at com.sun.javafx.binding.StringFormatter.format(Unknown Source)
    at javafx.beans.binding.Bindings.format(Unknown Source)
    at CustomButton.createButton(CustomButton.java:109)
    at CustomButton.<init>(CustomButton.java:26)
    at StartingPoint.start(StartingPoint.java:15)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(Unknown Source)
    ... 1 more
Exception running application StartingPoint

据我了解,这是因为CustomButton类的第109行有问题,就是这样:

button.styleProperty().bind(Bindings.format( // Set's the style of the button.

可能是什么问题?

【问题讨论】:

  • buttonSetOnAction 方法在CustomButton 类的第 105 行被调用。
  • 我在 Timeline 构造函数的调用中看到的唯一分号位于它的末尾。
  • @JavaNoob 在构成对Timeline 构造函数的调用的每一行末尾都有分号。至于错误,错误消息告诉您问题:格式需要一个整数作为参数之一,并且得到不同的东西。我建议阅读that methodFormatter 的文档
  • 哦。我忘了将编辑后的代码添加到问题中。对不起。

标签: java css animation javafx


【解决方案1】:

您可以在键值中使用数字(或其他可插值类型,例如颜色),并将字符串属性绑定到它们的值。看看这个例子:

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

public class AnimatedStyleButton extends Application {

    @Override
    public void start(Stage primaryStage) {
        Button button = new Button("Grow");
        ObjectProperty<Color> color = new SimpleObjectProperty<>(Color.RED);
        DoubleProperty size = new SimpleDoubleProperty(50);
        DoubleProperty fontSize = new SimpleDoubleProperty(12);
        DoubleProperty rotate = new SimpleDoubleProperty(0);
        StringProperty text = button.textProperty();
        BooleanProperty large = new SimpleBooleanProperty(false);

        Timeline timeline = new Timeline();

        button.setOnAction(e -> {

            timeline.getKeyFrames().clear();

            if (large.get()) {
                timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(1), 
                        new KeyValue(text, "Grow"),
                        new KeyValue(size, 80),
                        new KeyValue(large, false),
                        new KeyValue(rotate, 0),
                        new KeyValue(color, Color.RED),
                        new KeyValue(fontSize, 12)
                ));
            } else {
                timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(1), 
                        new KeyValue(text, "Shrink"),
                        new KeyValue(size, 200),
                        new KeyValue(large, true),
                        new KeyValue(rotate, 360),
                        new KeyValue(color, Color.BLUE),
                        new KeyValue(fontSize, 24)
                ));
            }

            timeline.play();
        });

        StringProperty colorAsString = new SimpleStringProperty();
        colorAsString.bind(Bindings.createStringBinding(() -> toWebColor(color.get()), color)); 

        button.styleProperty().bind(Bindings.format(
                "-fx-pref-width: %f;"
                + "-fx-pref-height: %f;"
                + "-fx-rotate: %f;"
                + "-fx-font-size: %f;"
                + "-fx-base: %s",
                size, size, rotate, fontSize, colorAsString));

        button.disableProperty().bind(timeline.statusProperty().isEqualTo(Animation.Status.RUNNING));

        StackPane root = new StackPane(button);
        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    private String toWebColor(Color c) {
        return String.format("#%02x%02x%02x", 
            (int) (c.getRed() * 255),
            (int) (c.getGreen() * 255),
            (int) (c.getBlue() * 255)
        );
    }

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

编辑:我可能应该查看linked question,因为此解决方案与此非常相似。无论如何,它可能会有所帮助。

【讨论】:

  • 嗨。感谢您的回答。我刚刚通过实现您的解决方案来编辑​​代码,但现在我在 CustomButton 类的第 109 行收到错误。我刚刚更新了问题。
【解决方案2】:

如果你在谈论 css 字符串属性,你可以这样使用它:-

Timeline animation = new Timeline(new KeyFrame(Duration.seconds(0.3),new KeyValue(myButton.translateYProperty(),-10,animationTime)),
            new KeyFrame(Duration.seconds(0.3),new KeyValue(myButton.styleProperty(), "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.09), 10, 0, 0, 8);" ,animationTime)));

    animation.play();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多