Optimizing Pagination rendering 优化分页渲染

huangapple go评论71阅读模式
英文:

Optimizing Pagination rendering

问题

在查看 Pagination 时,涉及到渲染复杂页面的问题。API 示例,et al.,通常会指定一个 pageFactory,每次调用它时都会简单地构建一个新的控件。确实,在分页时,下面的示例 进行性能分析 时,内存压力很小,会有大量新的实例被迅速回收。如果不断增加的复杂性改变了情况,我可以怎么做?

Optimizing Pagination rendering
优化分页渲染

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 https://stackoverflow.com/q/76359690/230513 */

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);
    }
}
英文:

While looking at Pagination, the question of rendering complex pages arose. The API example, et al., typically specify a pageFactory that simply constructs a new control each time it is called. Indeed, profiling the example below while paging showed minimal memory pressure, with a flurry of new instances that were promptly collected. What can I do if growing complexity changes the picture?

Optimizing Pagination rendering
优化分页渲染

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 https://stackoverflow.com/q/76359690/230513 */
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) (&#39;A&#39; + 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(&quot;PaginationTStreamest&quot;);
List&lt;Item&gt; items = Stream.generate(Item::ofRandom)
.limit(N).collect(Collectors.toList());
var pagination = new Pagination(items.size(), 0);
pagination.setPageFactory((i) -&gt; 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(&quot;-fx-font-family: serif; -fx-font-size: 36;&quot;);
label.setContentDisplay(ContentDisplay.CENTER);
var button = new Button(&quot;Button&quot;);
StackPane.setAlignment(button, Pos.BOTTOM_RIGHT);
button.setOnAction((e) -&gt; {
System.out.println(item);
});
pane.getChildren().addAll(label, button);
return pane;
}
public static void main(String[] args) {
launch(args);
}
}

答案1

得分: 5

在这种情况下,分析是正确的做法,但优化渲染器的准备工作可能是值得的。与ListViewTableView一样,Pagination使轻量级渲染变得容易:

  • 构建最少数量的可重用显示对象。

  • 在呼叫渲染时,_更新_现有对象以产生变化。

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

在注释中提供的_ad hoc_构造函数和工厂调用可用于比较。效益是可测量的,表明它应该能够很好地扩展。

相关的图表示例可以在这里看到,其他Pagination主题可以在这里找到。

注意事项:正如下面的评论中@Slaw所指出的,PaginationSkin对从一页到下一页的切换进行了动画处理。对于仅有一个视图组件的情况,动画是可用的但不太吸引人。此外,动画可能会干扰后续的更新。可能需要的一些可能的调整包括:

  • 禁用组件的动画,如下例所示:

      chart.setAnimated(false);
    
  • 安排组件的更新以在动画之后进行,如下所示:

      public Pane fill(int i, Item item) {
    Platform.runLater(() -&gt; {
    …
    });
    return pane;
    }
    

Optimizing Pagination rendering
优化分页渲染

PaginationTest.java

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.chart.PieChart;
import javafx.scene.control.Button;
import javafx.scene.control.Pagination;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

/** @see https://stackoverflow.com/a/76359691/230513 */
public class PaginationTest 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("PaginationTest");
        List<Item> items = Stream.generate(Item::ofRandom)
            .limit(N).collect(Collectors.toList());
        var pagination = new Pagination(items.size(), 0);
        var renderer = new RenderPane();
        pagination.setPageFactory((i) -> renderer.fill(items.get(i)));
//        pagination.setPageFactory((i) -> {
//            var renderer = new RenderPane(items.get(i));
//            return renderer.pane;
//        });
        stage.setScene(new Scene(pagination));
        stage.show();
        pagination.requestFocus();
    }

    private final static class RenderPane {

        private final StackPane pane = new StackPane();
        private final PieChart chart = new PieChart();
        private final Circle circle = new Circle(10);
        private final Button button = new Button("Details", circle);

        public RenderPane() {
            init();
        }

//        public RenderPane(Item item) {
//            init();
//            fill(item);
//        }

        private void init() {
            pane.setPadding(new Insets(16));
            StackPane.setAlignment(button, Pos.BOTTOM_RIGHT);
            chart.setAnimated(false);
            chart.getData().add(0, new PieChart.Data("Red", 0));
            chart.getData().add(1, new PieChart.Data("Green", 0));
            chart.getData().add(2, new PieChart.Data("Blue", 0));
            chart.getStylesheets().add("PaginationTest.css");
            button.setGraphic(circle);
            pane.getChildren().addAll(chart, button);
        }

        public Pane fill(Item item) {
            chart.setTitle(item.name);
            chart.getData().get(0).setPieValue(item.color.getRed());
            chart.getData().get(1).setPieValue(item.color.getGreen());
            chart.getData().get(2).setPieValue(item.color.getBlue());
            circle.setFill(item.color);
            button.setOnAction((e) -> {
                System.out.println(item);
            });
            return pane;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

PaginationTest.css

PaginationTest.css 
.default-color0.chart-pie { -fx-pie-color: #FF0000; }
.default-color1.chart-pie { -fx-pie-color: #00FF00; }
.default-color2.chart-pie { -fx-pie-color: #0000FF; }

<details>
<summary>英文:</summary>
Profiling is the correct thing to do in this case, but optimizing the renderer&#39;s preparation is probably warranted. Like `ListView` and `TableView`, `Pagination` makes [_flyweight_][1] rendering easy:
- Construct the minimal number of reusable display objects.
- When called to render, _update_ existing objects to effect a change.
In the `RenderPane` below, `init()` establishes a single pane, chart and button, while the `fill()` method invoked by the page factory&#39;s callback prepares the renderer with details of a specific `Item` for each page. Note that a _single_ chart and data collection suffices; the listening chart will update itself in response to the change.
An _ad hoc_ constructor and factory invocation in comments may be used for comparison. The benefit is measurable, suggesting it should scale well.
A related chart example is seen [here][2], and additional `Pagination` topics are addressed [here][3].
**Caveats**: As noted in comments by @Slaw below, the [`PaginationSkin`][4] animates the transition from one page to the next. With just a single view component, the animation is serviceable but less appealing. Moreover, the animation may interfere with subsequent updates. Among possible accommodations that may be required:
- Disable the component&#39;s animation, shown in the example below:
chart.setAnimated(false);
- Schedule the the component&#39;s update to follow the animation.
public Pane fill(int i, Item item) {
Platform.runLater(() -&gt; {
…
});
return pane;
[![Complex page][5]][5]
`PaginationTest.java`

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.chart.PieChart;
import javafx.scene.control.Button;
import javafx.scene.control.Pagination;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

/** @see https://stackoverflow.com/a/76359691/230513 */
public class PaginationTest 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) (&#39;A&#39; + 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(&quot;PaginationTest&quot;);
List&lt;Item&gt; items = Stream.generate(Item::ofRandom)
.limit(N).collect(Collectors.toList());
var pagination = new Pagination(items.size(), 0);
var renderer = new RenderPane();
pagination.setPageFactory((i) -&gt; renderer.fill(items.get(i)));

// pagination.setPageFactory((i) -> {
// var renderer = new RenderPane(items.get(i));
// return renderer.pane;
// });
stage.setScene(new Scene(pagination));
stage.show();
pagination.requestFocus();
}

private final static class RenderPane {
private final StackPane pane = new StackPane();
private final PieChart chart = new PieChart();
private final Circle circle = new Circle(10);
private final Button button = new Button(&quot;Details&quot;, circle);
public RenderPane() {
init();
}

// public RenderPane(Item item) {
// init();
// fill(item);
// }

    private void init() {
pane.setPadding(new Insets(16));
StackPane.setAlignment(button, Pos.BOTTOM_RIGHT);
chart.setAnimated(false);
chart.getData().add(0, new PieChart.Data(&quot;Red&quot;, 0));
chart.getData().add(1, new PieChart.Data(&quot;Green&quot;, 0));
chart.getData().add(2, new PieChart.Data(&quot;Blue&quot;, 0));
chart.getStylesheets().add(&quot;PaginationTest.css&quot;);
button.setGraphic(circle);
pane.getChildren().addAll(chart, button);
}
public Pane fill(Item item) {
chart.setTitle(item.name);
chart.getData().get(0).setPieValue(item.color.getRed());
chart.getData().get(1).setPieValue(item.color.getGreen());
chart.getData().get(2).setPieValue(item.color.getBlue());
circle.setFill(item.color);
button.setOnAction((e) -&gt; {
System.out.println(item);
});
return pane;
}
}
public static void main(String[] args) {
launch(args);
}

}


`PaginationTest.css`

PaginationTest.css
.default-color0.chart-pie { -fx-pie-color: #FF0000; }
.default-color1.chart-pie { -fx-pie-color: #00FF00; }
.default-color2.chart-pie { -fx-pie-color: #0000FF; }


[1]: https://en.wikipedia.org/wiki/Flyweight_pattern
[2]: https://stackoverflow.com/a/53579013/230513
[3]: https://stackoverflow.com/q/13872273/230513
[4]: https://github.com/openjdk/jfx/blob/f8c8a8a1f98902ab6c7923a9b0897106ea957ba8/modules/javafx.controls/src/main/java/javafx/scene/control/skin/PaginationSkin.java#L86
[5]: https://i.stack.imgur.com/sbQST.png
</details>

huangapple
  • 本文由 发表于 2023年5月30日 02:43:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76359690.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定