Java 将StringProperty用作KeyValue方法的参数

Java 将StringProperty用作KeyValue方法的参数,java,css,animation,javafx,Java,Css,Animation,Javafx,我试图通过使用CSS文件逐渐改变按钮的样式来制作一个动画按钮 我研究了一下,然后我开始问这个问题。我看到了被接受的答案,我试图实施那个人给出的解决方案 我所做的基本上是创建一个CustomButton类来扩展Button,然后设置StringProperties,其值根据是否单击按钮而变化;然后,将每个StringProperty连接起来,这样它们就可以作为CSS文件的替代,作为样式应用于按钮 我认为一切都编码得很好,问题是我无法创建在按下按钮时应该播放的动画,因为为此我需要创建一个时间线,其关

我试图通过使用CSS文件逐渐改变按钮的样式来制作一个动画按钮

我研究了一下,然后我开始问这个问题。我看到了被接受的答案,我试图实施那个人给出的解决方案

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

我认为一切都编码得很好,问题是我无法创建在按下按钮时应该播放的动画,因为为此我需要创建一个
时间线
,其
关键帧
键值
指定符合动画的每个组件的值,结果是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
有什么问题吗?

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

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);
    }
}
导入javafx.animation.animation;
导入javafx.animation.KeyFrame;
导入javafx.animation.KeyValue;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.beans.binding.Bindings;
导入javafx.beans.property.BooleanProperty;
导入javafx.beans.property.DoubleProperty;
导入javafx.beans.property.ObjectProperty;
导入javafx.beans.property.SimpleBoleAnProperty;
导入javafx.beans.property.SimpleDoubleProperty;
导入javafx.beans.property.SimpleObject属性;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.beans.property.StringProperty;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.layout.StackPane;
导入javafx.scene.paint.Color;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类AnimatedStyleButton扩展应用程序{
@凌驾
公共无效开始(阶段primaryStage){
按钮按钮=新按钮(“增长”);
ObjectProperty color=新的SimpleObjectProperty(color.RED);
DoubleProperty大小=新的SimpleDoubleProperty(50);
DoubleProperty fontSize=新的SimpleDoubleProperty(12);
DoubleProperty rotate=新的SimpleDoubleProperty(0);
StringProperty text=button.textProperty();
BooleanProperty large=新的SimpleBoleAnProperty(false);
时间线=新时间线();
按钮。设置操作(e->{
timeline.getKeyFrames().clear();
if(large.get()){
timeline.getKeyFrames().add(新的关键帧(持续时间)秒(1),
新键值(文本“增长”),
新键值(大小为80),
新键值(大,假),
新键值(旋转,0),
新的键值(颜色、颜色、红色),
新键值(fontSize,12)
));
}否则{
timeline.getKeyFrames().add(新的关键帧(持续时间)秒(1),
新键值(文本“收缩”),
新键值(大小,200),
新键值(大,真),
新键值(旋转,360),
新的键值(颜色、颜色、蓝色),
新键值(fontSize,24)
));
}
timeline.play();
});
StringProperty colorAsString=新的SimpleStringProperty();
colorAsString.bind(Bindings.createStringBinding(()->towerBColor(color.get()),color));
button.styleProperty().bind(Bindings.format(
“-fx pref宽度:%f;”
+“-fx pref高度:%f;”
+“-fx旋转:%f;”
+“-fx字体大小:%f;”
+“-外汇基数:%s”,
大小、大小、旋转、字体大小、颜色字符串);
button.disableProperty().bind(timeline.statusProperty().isEqualTo(Animation.Status.RUNNING));
StackPane根=新建StackPane(按钮);
场景=新场景(根,400400);
初级阶段。场景(场景);
primaryStage.show();
}
私人毛巾颜色(c色){
返回String.format(“#%02x%02x%02x”,
(int)(c.getRed()*255),
(int)(c.getGreen()*255),
(int)(c.getBlue()*255)
);
}
公共静态void main(字符串[]args){
发射(args);
}
}

编辑:我可能应该看看,因为这个解决方案非常类似。无论如何,它可能会有所帮助。

如果您正在谈论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();

你好谢谢你的回答。我刚刚通过实现您的解决方案编辑了代码,但是现在在
CustomButton
类的第109行中出现了一个错误。我刚刚更新了这个问题。
buttonAction
方法在
CustomButton
类的第105行中被调用。我在
时间线
构造函数的调用中看到的唯一分号是在它的末尾。@JavaNoob构成对
时间线
构造器。至于错误,错误消息告诉您
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();