This is just a code dump of stuff you can choose to study or ignore. I may or may not provide additional explanation or answer further questions about it.
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
public class CustomLabel extends Group {
public static final double
LABEL_WIDTH = 200,
LABEL_HEIGHT = 60;
private static final double
FONT_SIZE = 15,
TEXT_BASELINE_LEFT_X = 15,
TEXT_BASELINE_LEFT_Y = 15,
BORDER_INSETS = 9,
BORDER_WIDTH = 2;
private static final Font font = Font.font(
"monospace",
FontWeight.BOLD,
FONT_SIZE
);
public CustomLabel(String labelText) {
Text text = createText(
labelText
);
final double textWidth = text.getLayoutBounds().getWidth();
Path border = createBorder(
textWidth
);
Rectangle background = new Rectangle(
LABEL_WIDTH,
LABEL_HEIGHT
);
background.setFill(Color.TRANSPARENT);
getChildren().addAll(
background,
border,
text
);
}
private static Path createBorder(double textWidth) {
Path border = new Path(
new MoveTo(BORDER_INSETS, BORDER_INSETS),
new LineTo(TEXT_BASELINE_LEFT_X, BORDER_INSETS),
new MoveTo(TEXT_BASELINE_LEFT_X + textWidth, BORDER_INSETS),
new LineTo(LABEL_WIDTH - BORDER_INSETS, BORDER_INSETS),
new LineTo(LABEL_WIDTH - BORDER_INSETS, LABEL_HEIGHT - BORDER_INSETS),
new LineTo(BORDER_INSETS, LABEL_HEIGHT - BORDER_INSETS),
new LineTo(BORDER_INSETS, BORDER_INSETS)
);
border.setStrokeWidth(BORDER_WIDTH);
return border;
}
private static Text createText(String labelText) {
Text text = new Text(
TEXT_BASELINE_LEFT_X,
TEXT_BASELINE_LEFT_Y,
" " + labelText + " "
);
text.setFont(font);
return text;
}
}
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class SingleLabelApp extends Application {
@Override
public void start(Stage stage) {
stage.setScene(new Scene(new CustomLabel("hello, world")));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SingleLabelAppWithAnimatedGradient extends Application {
private static final double GRADIENT_RADIUS = 40;
private static final Duration ANIMATION_DURATION = Duration.seconds(10);
private DoubleProperty offset = new SimpleDoubleProperty(-GRADIENT_RADIUS);
@Override
public void start(Stage stage) {
Pane pane = new Pane(
new CustomLabel("hello, world")
);
animateBackground(pane);
stage.setScene(new Scene(pane));
stage.show();
}
private void animateBackground(Pane pane) {
offset.addListener(o -> refreshBackground(pane));
Timeline gradientAnimator = new Timeline(
new KeyFrame(
Duration.seconds(0),
new KeyValue(
offset,
- GRADIENT_RADIUS
)
),
new KeyFrame(
ANIMATION_DURATION,
new KeyValue(
offset,
GRADIENT_RADIUS + CustomLabel.LABEL_WIDTH
)
)
);
gradientAnimator.setCycleCount(Animation.INDEFINITE);
gradientAnimator.play();
}
private void refreshBackground(Pane pane) {
pane.setBackground(
Background.fill(
createGradient(
offset.get()
)
)
);
}
private static RadialGradient createGradient(double offset) {
RadialGradient gradient = new RadialGradient(
30,
.2,
offset,
GRADIENT_RADIUS / 2,
GRADIENT_RADIUS,
false,
CycleMethod.NO_CYCLE,
new Stop(0, Color.SKYBLUE),
new Stop(GRADIENT_RADIUS, Color.PINK)
);
return gradient;
}
public static void main(String[] args) {
launch();
}
}
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ManyLabelApp extends Application {
private static final int NUM_LABELS = 1_000;
@Override
public void start(Stage stage) {
Group lotsaLabels = new Group();
for (int i = 0; i < NUM_LABELS; i++) {
CustomLabel customLabel = new CustomLabel(
"Item %06d".formatted(i)
);
lotsaLabels.getChildren().add(customLabel);
customLabel.setLayoutY(i * CustomLabel.LABEL_HEIGHT);
}
stage.setScene(new Scene(lotsaLabels));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
public class LabelListAppWithCreatedNodes extends Application {
private static final int NUM_LABELS = 1_000;
@Override
public void start(Stage stage) {
List<String> labelStrings = new ArrayList<>(NUM_LABELS);
for (int i = 0; i < NUM_LABELS; i++) {
labelStrings.add("Item %06d".formatted(i));
}
ListView<String> labelListView = new ListView<>(
FXCollections.observableList(labelStrings)
);
labelListView.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
setGraphic(new CustomLabel(item)); // <- not good.
}
});
stage.setScene(new Scene(labelListView));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
public class ChangeableCustomLabel extends Group {
public static final double
LABEL_WIDTH = 200,
LABEL_HEIGHT = 60;
private static final double
FONT_SIZE = 15,
TEXT_BASELINE_LEFT_X = 15,
TEXT_BASELINE_LEFT_Y = 15,
BORDER_INSETS = 9,
BORDER_WIDTH = 2;
private static final Font font = Font.font(
"monospace",
FontWeight.BOLD,
FONT_SIZE
);
private final Text text = new Text();
private final Path border = new Path();
private final MoveTo textSkipper = new MoveTo();
private static int numCreated = 0;
public ChangeableCustomLabel(String labelText) {
initText(
labelText
);
final double textWidth = calculateTextWidth();
initBorder(
textWidth
);
Rectangle background = new Rectangle(
LABEL_WIDTH,
LABEL_HEIGHT
);
background.setFill(Color.TRANSPARENT);
getChildren().addAll(
background,
border,
text
);
System.out.println("Num custom labels created: " + ++numCreated);
}
public void setLabelText(String labelText) {
text.setText(" " + labelText + " ");
textSkipper.setX(TEXT_BASELINE_LEFT_X + calculateTextWidth());
}
private void initBorder(double textWidth) {
textSkipper.setX(TEXT_BASELINE_LEFT_X + textWidth);
textSkipper.setY(BORDER_INSETS);
border.getElements().addAll(
new MoveTo(BORDER_INSETS, BORDER_INSETS),
new LineTo(TEXT_BASELINE_LEFT_X, BORDER_INSETS),
textSkipper,
new LineTo(LABEL_WIDTH - BORDER_INSETS, BORDER_INSETS),
new LineTo(LABEL_WIDTH - BORDER_INSETS, LABEL_HEIGHT - BORDER_INSETS),
new LineTo(BORDER_INSETS, LABEL_HEIGHT - BORDER_INSETS),
new LineTo(BORDER_INSETS, BORDER_INSETS)
);
border.setStrokeWidth(BORDER_WIDTH);
}
private void initText(String labelText) {
text.setX(TEXT_BASELINE_LEFT_X);
text.setY(TEXT_BASELINE_LEFT_Y);
text.setText(labelText);
text.setFont(font);
}
private double calculateTextWidth() {
return text.getLayoutBounds().getWidth();
}
}
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
public class LabelListAppWithCachedNodes extends Application {
private static final int NUM_LABELS = 100_000;
@Override
public void start(Stage stage) {
List<String> labelStrings = new ArrayList<>(NUM_LABELS);
for (int i = 0; i < NUM_LABELS; i++) {
labelStrings.add("Item %06d".formatted(i));
}
ListView<String> labelListView = new ListView<>(
FXCollections.observableList(labelStrings)
);
labelListView.setCellFactory(param -> new ListCell<>() {
final ChangeableCustomLabel changeableCustomLabel = new ChangeableCustomLabel(
""
);
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
changeableCustomLabel.setLabelText(item);
setGraphic(changeableCustomLabel);
}
});
stage.setScene(new Scene(labelListView));
stage.show();
}
public static void main(String[] args) {
launch();
}
}