我一直在尝试编写一种有效的“标签”控件,以垂直滚动方式(字幕/行情指示器)呈现文本。 我已经写了2个版本(以我在这里看到的一些模式为基础)来尝试提高性能,但是我希望有人可以指出一种更好的方法,因为这两个都使用大约4-9%的CPU。
PC规格:(Intel i7-7660U @ 2.50GHz)/ 16GB RAM /图形卡:Intel(R)Iris(TM)Plus图形640 / Win10。
虽然这是可以接受的,但那只是我希望我的应用程序执行的所有操作,它们将被用于现有的大型复杂应用程序中,并且在任何其他时间,除了其他详细视图之外,还有30多个可见的内容。< / p>
我尝试的第一个版本使用TimeLine,并且它与我想要的外观最接近。它仍然需要完善,以使文本根据需要显示/消失,但足够接近以进行基准测试。
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);
}
}
}
}
第二个版本使用Canvas和GraphicsContext。我以前没有使用过JavaFX Canvas,但我希望它可以像Swing / AWT一样,可以在其中使用一些屏幕外缓冲来提高性能,但不幸的是,此版本的性能不如TimeLine。在切换到下面显示的AnimationTimer之前,我曾尝试使用自己的Task / Service类来执行渲染,但是性能较差。希望我缺少有关应如何使用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++;
}
}
}
}
}
在此先感谢您的帮助。