JavaFX技巧20:有很多需要展示的地方吗? 使用画布!

JavaFX应用程序似乎有两种:第一种使用带有节点和CSS样式的场景图,第二种使用单个画布。 但是,将这两种方法混合使用是完全合法的。 尤其是当您的应用程序必须显示大量详细信息时,您很容易最终创建成千上万个节点。 即使JavaFX的整体性能非常出色,当所有这些节点都需要样式设置时(特别是由于可视化的动态性质而需要反复进行样式设置时),您很有可能会使系统崩溃。

对我来说,这是一个顿悟,当我意识到在FlexGanttFX中保证高性能的唯一方法是对每个包含画布的单元使用ListView。 不幸的是,该框架的代码太复杂,无法在一个小博客中与您共享。因此,我写了一个小例子来说明基本概念。 下图显示了运行示例时的结果。 ListView显示的数据涵盖了我生命中的岁月,并且每年的每一天都有随机生成的值。

bildschirmfoto-2015-06-15-um-19-25-50

最重要的类称为CanvasCell 。 它是一个专门的列表视图单元,其中包含标签和画布。 标签用于显示年份,画布用于绘制图表。

import java.util.Collections;
import java.util.List;

import javafx.geometry.Pos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;

public class CanvasCell extends ListCell<YearEntry> {

	private Label yearLabel;
	private ResizableCanvas canvas;

	public CanvasCell() {
		/*
		 * Important, otherwise we will keep seeing a horizontal scrollbar.
		 */
		setStyle("-fx-padding: 0px;");

		yearLabel = new Label();
		yearLabel
		  .setStyle("-fx-padding: 10px; -fx-font-size: 1.2em; -fx-font-weight: bold;");
		StackPane.setAlignment(yearLabel, Pos.TOP_LEFT);

		/*
		 * Create a resizable canvas and bind its width and height to the width
		 * and height of the table cell.
		 */
		canvas = new ResizableCanvas();
		canvas.widthProperty().bind(widthProperty());
		canvas.heightProperty().bind(heightProperty());

		StackPane pane = new StackPane();
		pane.getChildren().addAll(yearLabel, canvas);

		setGraphic(pane);
		setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
	}

	@Override
	protected void updateItem(YearEntry entry, boolean empty) {
		if (empty || entry == null) {
			yearLabel.setText("");
			canvas.setData(Collections.emptyList());
			canvas.draw();
		} else {
			yearLabel.setText(Integer.toString(entry.getYear()));
			canvas.setData(entry.getValues());
			canvas.draw();
		}
	}

	/*
	 * Canvas is normally not resizable but by overriding isResizable() and
	 * binding its width and height to the width and height of the cell it will
	 * automatically resize.
	 */
	class ResizableCanvas extends Canvas {

		private List<Double> data = Collections.emptyList();

		public ResizableCanvas() {

			/*
			 * Make sure the canvas draws its content again when its size
			 * changes.
			 */
			widthProperty().addListener(it -> draw());
			heightProperty().addListener(it -> draw());
		}

		@Override
		public boolean isResizable() {
			return true;
		}

		@Override
		public double prefWidth(double height) {
			return getWidth();
		}

		@Override
		public double prefHeight(double width) {
			return getHeight();
		}

		public void setData(List<Double> data) {
			this.data = data;
		}

		/*
		 * Draw a chart based on the data provided by the model.
		 */
		private void draw() {
			GraphicsContext gc = getGraphicsContext2D();
			gc.clearRect(0, 0, getWidth(), getHeight());

			Stop[] stops = new Stop[] { new Stop(0, Color.SKYBLUE),
					new Stop(1, Color.SKYBLUE.darker().darker()) };
			LinearGradient gradient = new LinearGradient(0, 0, 0, 300, false,
					CycleMethod.NO_CYCLE, stops);

			gc.setFill(gradient);

			double availableHeight = getHeight() * .8;
			double counter = 0;
			for (Double value : data) {
				double x = getWidth() / 365 * counter;
				double barHeight = availableHeight * value / 100;
				double barWidth = getWidth() / 365 + 1;
				gc.fillRect(x, getHeight() - barHeight, barWidth, barHeight);
				counter++;
			}
		}
	}
}

对于数据,我们使用一个非常简单的类来存储年份和值列表。

import java.util.ArrayList;
import java.util.List;


/**
 * Just some fake model object.
 */
public class YearEntry {

	private int year;

	public YearEntry(int year) {
		this.year = year;
	}

	public int getYear() {
		return year;
	}

	private List<Double> values = new ArrayList<>();

	/**
	 * Stores the values shown in the chart.
	 */
	public List<Double> getValues() {
		return values;
	}
}

以下清单显示了主类。

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Stage;

public class CanvasApp extends Application {

	@Override
	public void start(Stage stage) throws Exception {

		/*
		 * Create some random data for my life span.
		 */
		ObservableList<YearEntry> data = 
			FXCollections.observableArrayList();
		for (int year = 1969; year < 2015; year++) {
			YearEntry entry = new YearEntry(year);
			for (int day = 0; day < 365; day++) {
				entry.getValues().add(Math.random() * 100);
			}
			data.add(entry);
		}

		ListView<YearEntry> listView = new ListView<>(data);
		listView.setCellFactory(param -> new CanvasCell());
		listView.setFixedCellSize(200);

		Scene scene = new Scene(listView);

		stage.setTitle("Canvas Cell");
		stage.setScene(scene);
		stage.setWidth(600);
		stage.setHeight(600);
		stage.show();
	}

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

翻译自: https://www.javacodegeeks.com/2015/06/javafx-tip-20-a-lot-to-show-use-canvas.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值