优化分页渲染

tra*_*god 3 java pagination javafx renderer

在查看时Pagination,出现了渲染复杂页面的问题。API示例,通常指定pageFactory每次调用时简单构造一个新控件的 a。事实上,在分页时对下面的示例进行分析显示内存压力最小,并且迅速收集了一系列新实例。如果日益复杂的情况改变了情况,我该怎么办?

简单页面

import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Pagination;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

/** @see /sf/ask/5345178331/ */

public class PaginationSample extends Application {

    private static final int N = 100;

    private record Item(String name, Color color) {

        private static final Random r = new Random();

        public static Item ofRandom() {
            var s = (char) ('A' + r.nextInt(26))
                + String.valueOf(r.nextInt(900_000) + 100_000);
            var c = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255));
            return new Item(s, c);
        }
    }

    @Override
    public void start(Stage stage) {
        stage.setTitle("PaginationTStreamest");
        List<Item> items = Stream.generate(Item::ofRandom)
            .limit(N).collect(Collectors.toList());
        var pagination = new Pagination(items.size(), 0);
        pagination.setPageFactory((i) -> createItemPane(items.get(i)));
        stage.setScene(new Scene(pagination));
        stage.show();
        pagination.requestFocus();
    }

    private StackPane createItemPane(Item item) {
        var pane = new StackPane();
        pane.setPadding(new Insets(16));
        var label = new Label(item.name, new Rectangle(320, 240, item.color));
        label.setTextFill(item.color.invert());
        label.setStyle("-fx-font-family: serif; -fx-font-size: 36;");
        label.setContentDisplay(ContentDisplay.CENTER);
        var button = new Button("Button");
        StackPane.setAlignment(button, Pos.BOTTOM_RIGHT);
        button.setOnAction((e) -> {
            System.out.println(item);
        });
        pane.getChildren().addAll(label, button);
        return pane;
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

tra*_*god 5

在这种情况下,分析是正确的做法,但优化渲染器的准备工作可能是有必要的。与ListView和一样TableViewPagination使享元渲染变得容易:

\n
    \n
  • 构造最少数量的可重用显示对象。

    \n
  • \n
  • 当调用渲染时,更新现有对象以实现更改。

    \n
  • \n
\n

RenderPane下面,init()建立单个窗格、图表和按钮,而fill()页面工厂的回调调用的方法为渲染器准备Item每个页面的特定详细信息。请注意,单个图表和数据收集就足够了;聆听图表将根据变化自行更新。

\n

注释中的临时构造函数和工厂调用可用于比较。好处是可以衡量的,表明它应该可以很好地扩展。

\n

相关图表示例请参见此处,其他Pagination主题也请参见此处

\n

注意事项:正如下面 @Slaw 的评论所述,PaginationSkin动画从一页到下一页的过渡。仅使用单个视图组件,动画就可以使用,但吸引力较差。而且,动画可能会干扰后续更新。可能需要的便利包括:

\n\n

复杂页面

\n

PaginationTest.java

\n
import java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.PieChart;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Pagination;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\nimport javafx.stage.Stage;\n\n/** @see /sf/answers/5345178401/ */\npublic class PaginationTest extends Application {\n\n    private static final int N = 100;\n\n    private record Item(String name, Color color) {\n\n        private static final Random r = new Random();\n\n        public static Item ofRandom() {\n            var s = (char) (\'A\' + r.nextInt(26))\n                + String.valueOf(r.nextInt(900_000) + 100_000);\n            var c = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255));\n            return new Item(s, c);\n        }\n    }\n\n    @Override\n    public void start(Stage stage) {\n        stage.setTitle("PaginationTest");\n        List<Item> items = Stream.generate(Item::ofRandom)\n            .limit(N).collect(Collectors.toList());\n        var pagination = new Pagination(items.size(), 0);\n        var renderer = new RenderPane();\n        pagination.setPageFactory((i) -> renderer.fill(items.get(i)));\n//        pagination.setPageFactory((i) -> {\n//            var renderer = new RenderPane(items.get(i));\n//            return renderer.pane;\n//        });\n        stage.setScene(new Scene(pagination));\n        stage.show();\n        pagination.requestFocus();\n    }\n\n    private final static class RenderPane {\n\n        private final StackPane pane = new StackPane();\n        private final PieChart chart = new PieChart();\n        private final Circle circle = new Circle(10);\n        private final Button button = new Button("Details", circle);\n\n        public RenderPane() {\n            init();\n        }\n\n//        public RenderPane(Item item) {\n//            init();\n//            fill(item);\n//        }\n\n        private void init() {\n            pane.setPadding(new Insets(16));\n            StackPane.setAlignment(button, Pos.BOTTOM_RIGHT);\n            chart.setAnimated(false);\n            chart.getData().add(0, new PieChart.Data("Red", 0));\n            chart.getData().add(1, new PieChart.Data("Green", 0));\n            chart.getData().add(2, new PieChart.Data("Blue", 0));\n            chart.getStylesheets().add("PaginationTest.css");\n            button.setGraphic(circle);\n            pane.getChildren().addAll(chart, button);\n        }\n\n        public Pane fill(Item item) {\n            chart.setTitle(item.name);\n            chart.getData().get(0).setPieValue(item.color.getRed());\n            chart.getData().get(1).setPieValue(item.color.getGreen());\n            chart.getData().get(2).setPieValue(item.color.getBlue());\n            circle.setFill(item.color);\n            button.setOnAction((e) -> {\n                System.out.println(item);\n            });\n            return pane;\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

PaginationTest.css

\n
PaginationTest.css \n.default-color0.chart-pie { -fx-pie-color: #FF0000; }\n.default-color1.chart-pie { -fx-pie-color: #00FF00; }\n.default-color2.chart-pie { -fx-pie-color: #0000FF; }\n
Run Code Online (Sandbox Code Playgroud)\n

  • 在内部,“PaginationSkin”保留“当前窗格”和“下一个窗格”。这些是堆栈窗格,包含页面工厂返回的节点。这些窗格是动画的。当您导航到另一个页面时,将调用页面工厂,并将返回的节点放入“下一个窗格”中。动画完成后,“下一个窗格”将变为“当前窗格”,反之亦然。通过每次从页面工厂返回 _same_ 节点,该节点会从“当前窗格”中隐式删除,因为一个节点不能属于两个父节点。我猜这会扰乱图表的动画。 (2认同)