我有一个涟漪按钮,一旦点击它,就会播放涟漪效果动画。
在按钮的皮肤内部,有一个 Circle
,在程序开始执行时,其不透明度设置为 0
,单击按钮后,不透明度设置为1
按钮的半径变大。
有一个DoubleBinding
绑定(bind),其定义如下:
DoubleBinding circleRippleRadius = new DoubleBinding() {
{
bind(heightProperty(), widthProperty()); // Sets the parameters of the bind method.
}
@Override // Overrides the computeValue method, which computes the value of the binding.
protected double computeValue() {
return Math.max(heightProperty().get(), widthProperty().get()); // Return the greatest of both numbers
}
};
我不想使用按钮的 heightProperty
和 widthProperty
属性,而是想使用添加了按钮的场景的高度和宽度,因为我想单击按钮填满整个屏幕后出现的圆圈。
我怎样才能做到这一点?
更新:这是定义动画组件值和动画本身的代码:
private void createRippleEffect(Circle circleRipple) {
circleRipple.setOpacity(1.0); // Sets the opacity of the circleRipple to 0, since it must not be showed yet.
/*Fade Transition*/
FadeTransition fadeTransition = new FadeTransition(RIPPLE_DURATION, circleRipple);
fadeTransition.setInterpolator(Interpolator.EASE_OUT);
fadeTransition.setFromValue(1.0); // Sets the opacity to %100
fadeTransition.setToValue(1.0); // The opacity doesn't change.
/*Scale Transition*/
Timeline scaleRippleTimeline = new Timeline();
NumberBinding circleRippleRadius =
Bindings.max(Bindings.selectDouble(sceneProperty(), "width"),
Bindings.selectDouble(sceneProperty(), "height"));
circleRippleRadius.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { // Each time it changes
KeyValue scaleValue = new KeyValue(circleRipple.radiusProperty(), newValue, Interpolator.EASE_OUT);
KeyFrame scaleFrame = new KeyFrame(RIPPLE_DURATION, scaleValue);
scaleRippleTimeline.getKeyFrames().add(scaleFrame);
});
private void createRippleEffect(Circle circleRipple) {
circleRipple.setOpacity(1.0); // Sets the opacity of the circleRipple to 0, since it must not be showed yet.
/*Fade Transition*/
FadeTransition fadeTransition = new FadeTransition(RIPPLE_DURATION, circleRipple);
fadeTransition.setInterpolator(Interpolator.EASE_OUT);
fadeTransition.setFromValue(1.0); // Sets the opacity to %100
fadeTransition.setToValue(1.0); // The opacity doesn't change.
/*Scale Transition*/
Timeline scaleRippleTimeline = new Timeline();
NumberBinding circleRippleRadius =
Bindings.max(Bindings.selectDouble(sceneProperty(), "width"),
Bindings.selectDouble(sceneProperty(), "height"));
circleRippleRadius.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { // Each time it changes
KeyValue scaleValue = new KeyValue(circleRipple.radiusProperty(), newValue, Interpolator.EASE_OUT);
KeyFrame scaleFrame = new KeyFrame(RIPPLE_DURATION, scaleValue);
scaleRippleTimeline.getKeyFrames().add(scaleFrame);
});
SequentialTransition rippleTransition = new SequentialTransition(); // The circle must change its opacity and scale at the same time
rippleTransition.getChildren().addAll(
scaleRippleTimeline,
fadeTransition
);
ParallelTransition parallelTransition = new ParallelTransition();
getStyleClass().addListener((ListChangeListener.Change<? extends String> c) -> { // Don't pay attention to this. The style changes if
// the CSS file has "toggle" or "flat" inside its list,
// but these are never added so it doesn't matter.
if (c.getList().indexOf("flat") == -1 && c.getList().indexOf("toggle") == -1) {
setMinWidth(88);
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.30), 5, 0.10, 0, 2));
parallelTransition.getChildren().addAll(rippleTransition); // parallelTransition is basically the same as rippleTransition, since
// "toggle" and "flat" are never added to the CSS's list.
} else {
parallelTransition.getChildren().addAll(rippleTransition);
setMinWidth(USE_COMPUTED_SIZE);
setEffect(null);
}
});
this.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
parallelTransition.stop(); // In case that the parallelTransition is already running, stop it.
circleRipple.setOpacity(0.0); // Sets the opacity of the circle to 0, since the animation must play from he beginning.
circleRipple.setRadius(0.1); // Sets the radius to 0.1 for the same reason as the circle's opacity.
circleRipple.setCenterX(event.getX()); // The center of the circle is the location in which the mouse was clicked.
circleRipple.setCenterY(event.getY()); // The center of the circle is the location in which the mouse was clicked.
parallelTransition.playFromStart(); // Plays the animation.
});
}
这是定义按钮皮肤的方法:
@Override
public Skin<?> createDefaultSkin() {
ButtonSkin buttonSkin = getButtonSkin();
if (buttonSkin == null) {
buttonSkin = new ButtonSkin(this);
Circle circleRipple = new Circle(0.1, RIPPLE_COLOR);
buttonSkin.getChildren().add(0, circleRipple);
setSkin(buttonSkin);
createRippleEffect(circleRipple);
getStyleClass().add("ripple-button"); // What the CSS does is changing the button's color and text size, nothing important.
}
return buttonSkin;
}
提前致谢。
解决方案:
circleRippleRadius
的值小于舞台的值的问题可能是由于舞台的大小与场景的大小不同。我以前不知道,现在我知道了。
为了让按钮的圆圈填满整个屏幕,我所要做的就是将舞台的 widthProperty
和 heightProperty
属性作为参数传递给按钮的构造函数。
在按钮的类中,我为宽度和高度创建了两个 ReadOnlyDoubleProperty
属性,在创建按钮之前它们都是空的;在这种情况下,定义的 ReadOnlyDoubleProperty
属性的值将被作为参数传递的 widthProperty
和 heightProperty
属性的值覆盖。
完成后,我所要做的就是为每个属性添加一个监听器,并在每次其中一个更改时将 circleRippleRadius
的值更改为属性值中较大的一个。
最佳答案
您不应该扩展 DoubleBinding。相反,您应该从现有的工厂方法创建绑定(bind):
NumberBinding circleRippleRadius =
Bindings.max(widthProperty(), heightProperty());
// Optional
DoubleExpression circleRippleRadiusAsDouble =
DoubleExpression.doubleExpression(circleRippleRadius);
要绑定(bind)到场景属性,您需要使用 Bindings.selectDouble ,它可以处理按钮的初始 null 场景属性:
NumberBinding size =
Bindings.max(
Bindings.selectDouble(button.sceneProperty(), "width"),
Bindings.selectDouble(button.sceneProperty(), "height"));
关于java - 获取在控件的类定义中添加自定义控件的场景的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38016557/