JavaFX时间线与画布文本动画
我一直在尝试编写一个高效的“标签”控件,以垂直滚动方式呈现文本(选框/标记)。 我已经编写了两个版本(大致基于我在这里看到的一些模式)来尝试提高性能,但我希望有人能给我指出一个更好的方法,因为这两个版本都使用了大约4-9%的CPU PC规格:(英特尔i7-7660U@2.50GHz)/16GB内存/图形卡:英特尔(R)虹膜(TM)Plus图形640/Win10 虽然这是可以接受的,如果这是我希望我的应用程序所做的一切,它们将用于现有的大型和复杂的应用程序中,并且在任何时候都可以看到30+个细节视图 我尝试的第一个版本使用了TimeLine,最接近我想要的外观。它仍然需要改进,以便文本按需要显示/消失,但它足够接近基准测试JavaFX时间线与画布文本动画,javafx,javafx-8,Javafx,Javafx 8,我一直在尝试编写一个高效的“标签”控件,以垂直滚动方式呈现文本(选框/标记)。 我已经编写了两个版本(大致基于我在这里看到的一些模式)来尝试提高性能,但我希望有人能给我指出一个更好的方法,因为这两个版本都使用了大约4-9%的CPU PC规格:(英特尔i7-7660U@2.50GHz)/16GB内存/图形卡:英特尔(R)虹膜(TM)Plus图形640/Win10 虽然这是可以接受的,如果这是我希望我的应用程序所做的一切,它们将用于现有的大型和复杂的应用程序中,并且在任何时候都可以看到30+个细节视
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.CacheHint;
import javafx.scene.Scene;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TickerLabelTester extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
List<String> list1 = new ArrayList<String>(
Arrays.asList("Name 1",
"Name 2"));
List<String> list2 = new ArrayList<String>(
Arrays.asList("Name 3",
"Name 4"));
VBox root = new VBox(2.0);
root.setStyle("-fx-background-color:orange;");
for (int i = 0; i < 30; i++) {
TickerLabel tickerLabel = new TickerLabel();
if (i % 2 == 0) {
tickerLabel.setStrings(list1);
} else {
tickerLabel.setStrings(list2);
}
HBox hBox = new HBox(tickerLabel);
hBox.setBorder(new Border(new BorderStroke(Color.BLACK,
BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
root.getChildren().add(hBox);
}
root.setPrefSize(250, 700);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Scrolling Strings");
stage.show();
}
class TickerLabel extends Pane {
private Timeline timeline;
private Text text;
private List<String> strings = new ArrayList<String>();
public TickerLabel() {
Rectangle clip = new Rectangle();
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
clip.setWidth(newValue.getWidth());
clip.setHeight(newValue.getHeight());
});
this.setClip(clip);
text = new Text();
this.getChildren().add(text);
text.setCache(true);
text.setCacheHint(CacheHint.SPEED);
timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
}
public void setStrings(List<String> strings) {
this.strings = strings;
rerunTransition();
}
private void rerunTransition() {
timeline.stop();
if (strings.size() > 0) {
recalculateTranslateTransition();
timeline.playFromStart();
}
}
private void recalculateTranslateTransition() {
Duration duration = Duration.ZERO;
double startPos = text.getLayoutBounds().getHeight() + 8;
for (String string : strings) {
KeyValue initKeyValue = new KeyValue(text.translateYProperty(), startPos);
KeyValue initTextKeyValue = new KeyValue(text.textProperty(), string);
KeyFrame initFrame = new KeyFrame(duration, initKeyValue, initTextKeyValue);
timeline.getKeyFrames().add(initFrame);
duration = Duration.seconds(duration.toSeconds() + 2);
KeyValue endKeyValue = new KeyValue(text.translateYProperty(), 0);
KeyFrame endFrame = new KeyFrame(duration, endKeyValue);
timeline.getKeyFrames().add(endFrame);
}
}
}
}
import java.util.ArrayList;
导入java.util.array;
导入java.util.List;
导入javafx.animation.KeyFrame;
导入javafx.animation.KeyValue;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.scene.CacheHint;
导入javafx.scene.scene;
导入javafx.scene.layout.Border;
导入javafx.scene.layout.BorderStroke;
导入javafx.scene.layout.BorderStrokeStyle;
导入javafx.scene.layout.BorderWidths;
导入javafx.scene.layout.CornerRadii;
导入javafx.scene.layout.HBox;
导入javafx.scene.layout.Pane;
导入javafx.scene.layout.VBox;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.scene.text.text;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类TickerLabelTester扩展应用程序{
公共静态void main(字符串[]args){
应用程序启动(args);
}
@凌驾
公众假期开始(阶段){
List list1=新阵列列表(
Arrays.asList(“名称1”,
“名称2”);
List list2=新的阵列列表(
Arrays.asList(“名称3”,
"姓名(4);;
VBox根=新的VBox(2.0);
root.setStyle(“-fx背景色:橙色;”);
对于(int i=0;i<30;i++){
TickerLabel TickerLabel=新的TickerLabel();
如果(i%2==0){
股票标签设置字符串(列表1);
}否则{
股票标签设置字符串(列表2);
}
HBox HBox=新的HBox(标签);
hBox.setOrder(新边框)(新边框笔划)(颜色为黑色,
BorderStrokeStyle.SOLID,CornerRadii.EMPTY,BorderWidths.DEFAULT));
root.getChildren().add(hBox);
}
root.setPrefSize(250700);
场景=新场景(根);
舞台场景;
stage.setTitle(“滚动字符串”);
stage.show();
}
类TickerLabel扩展窗格{
私人时间表;
私人文本;
私有列表字符串=新的ArrayList();
公共股票标签(){
矩形剪辑=新矩形();
this.layoutBoundsProperty().addListener((可观察、旧值、新值)->{
clip.setWidth(newValue.getWidth());
setHeight(newValue.getHeight());
});
这个.setClip(clip);
text=新文本();
this.getChildren().add(文本);
text.setCache(true);
setCacheHint(CacheHint.SPEED);
时间线=新时间线();
timeline.setCycleCount(timeline.unfinite);
}
公共无效设置字符串(列表字符串){
this.strings=字符串;
重新翻译();
}
私有无效重新转换(){
timeline.stop();
if(strings.size()>0){
重新计算TranslateTransition();
timeline.playFromStart();
}
}
私有void重新计算TranslateTransition(){
持续时间=持续时间0;
double startPos=text.getLayoutBounds().getHeight()+8;
for(字符串:字符串){
KeyValue initKeyValue=新的KeyValue(text.translateYProperty(),startPos);
KeyValue initTextKeyValue=新的KeyValue(text.textProperty(),字符串);
KeyFrame initFrame=新关键帧(持续时间、initKeyValue、initTextKeyValue);
timeline.getKeyFrames().add(initFrame);
持续时间=持续时间.s(持续时间.toSeconds()+2);
KeyValue endKeyValue=新的KeyValue(text.translateYProperty(),0);
关键帧endFrame=新关键帧(持续时间,endKeyValue);
timeline.getKeyFrames().add(endFrame);
}
}
}
}
第二个版本使用画布和GraphicsContext。我以前没有使用过JavaFXCanvas,我希望它可能像Swing/AWT一样,我可以使用一些屏幕外缓冲来提高性能,不幸的是,这个版本的性能不如时间表。在切换到下面显示的AnimationTimer之前,我曾尝试使用自己的任务/服务类来执行渲染,但性能较差。希望我遗漏了一些关于如何使用GraphicsContext的内容
在实际应用程序中,每个标签的字符串列表将不时单独更新,因此时间线或AnimationTimer需要选择列表更改
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.CacheHint;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TickerCanvasLabelTester extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
List<String> list1 = new ArrayList<String>(
Arrays.asList("Name 1",
"Name 2"));
List<String> list2 = new ArrayList<String>(
Arrays.asList("Name 3",
"Name 4"));
VBox root = new VBox(2.0);
root.setStyle("-fx-background-color:orange;");
for (int i = 0; i < 30; i++) {
TickerLabel tickerLabel = new TickerLabel();
if (i % 2 == 0) {
tickerLabel.setStrings(list1);
} else {
tickerLabel.setStrings(list2);
}
HBox hBox = new HBox(tickerLabel);
hBox.setBorder(new Border(new BorderStroke(Color.BLACK,
BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
root.getChildren().add(hBox);
}
root.setPrefSize(250, 700);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Scrolling Strings");
stage.show();
}
class TickerLabel extends Pane {
private List<String> strings = new ArrayList<String>();
private int stringPointer = 0;
private Canvas canvas;
private GraphicsContext gc;
private AnimationTimer at;
private String string;
public TickerLabel() {
Rectangle clip = new Rectangle();
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
clip.setWidth(newValue.getWidth());
clip.setHeight(newValue.getHeight());
});
this.setClip(clip);
canvas = new Canvas();
canvas.setCache(true);
canvas.setCacheHint(CacheHint.SPEED);
canvas.setWidth(100);
canvas.setHeight(20);
gc = canvas.getGraphicsContext2D();
string = "";
at = new AnimationTimer() {
private long lastUpdate = 0;
private double i = canvas.getHeight() + 2;
@Override
public void handle(long now) {
// limit how often this is called
if (now - lastUpdate >= 200_000_000) {
if (i >= -2) {
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.strokeText(string, 0, i);
i--;
} else {
i = canvas.getHeight();
selectNextString();
}
lastUpdate = now;
}
}
};
this.getChildren().add(canvas);
}
public void setStrings(List<String> strings) {
this.strings = strings;
at.stop();
selectNextString();
at.start();
}
private void selectNextString() {
if (strings.size() > 0) {
string = strings.get(stringPointer);
if (stringPointer >= strings.size() - 1) {
stringPointer = 0;
} else {
stringPointer++;
}
}
}
}
}
import java.util.ArrayList;
导入java.util.array;
导入java.util.List;
导入javafx.animation.AnimationTimer;
导入javafx.application.application;
导入javafx.scene.CacheHint;
导入javafx.scene.scene;
导入javafx.scene.canvas.canvas;
导入javafx.scene.canvas.GraphicsContext;
导入javafx.scene.layout.Border;
导入javafx.scene.layout.BorderStroke;
导入javafx.scene.layout.BorderStrokeStyle;
导入javafx.scene.layout.BorderWidths;
导入javafx.scene.layout.CornerRadii;
导入javafx.scene.layout。