使用JavaFX图表,我需要反转堆积区域图表的y轴,以便正零位于顶部,正数位于y轴向下。下面是我想要实现的模型。
在JavaFX中实现这一目标的最佳方法是什么(阅读:最短的开发时间和高代码重用)?
更新
将数据转换为负数不是一种选择。我正在寻找可以使用正数的答案,“未触动过。”
答案 0 :(得分:2)
您可以使用带有负值的常规轴,但添加TickLabelFormatter
将剥离减号。
final NumberAxis yAxis = new NumberAxis(-25, 0, 5);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
@Override
public String toString(Number value) {
// note we are printing minus value
return String.format("%7.1f", -value.doubleValue());
}
});
series1.getData().add(new XYChart.Data("Jan", -1));
series1.getData().add(new XYChart.Data("Feb", -5));
series1.getData().add(new XYChart.Data("Mar", -20));
答案 1 :(得分:0)
不简单的选项是在轴上启用inverse()
操作。如果没有修补JRE类,那就太复杂了。
有关如何处理此问题的一些提示:
1)扩展ValueAxis或Axis(不幸的是,NumberAxis是最终的)
2)将boolean field和inverse()
方法添加到轴类
public void inverse() {
inversed = !inversed; // boolean property
invalidateRange();
requestAxisLayout();
}
3)如果扩展ValueAxis - 你需要补偿超类所应用的偏移量(并截取轴大小正在变化的代码)
@Override
public Long getValueForDisplay(double displayPosition) {
if (inversed)
return super.getValueForDisplay(offset - displayPosition);
else
return super.getValueForDisplay(displayPosition);
}
@Override
public double getDisplayPosition(Long value) {
if (inversed)
return offset - super.getDisplayPosition(value);
else
return super.getDisplayPosition(value);
}
4)(最丑陋的部分)取消隐藏由Axis类抑制的标签刻度 - 原始实现取决于刻度的默认顺序。我没有找到任何其他方式通过反射解锁它。所以这很脆弱。
@Override
protected void layoutChildren() {
final Side side = getSide();
boolean isHorisontal = null == side || side.isHorizontal();
this.offset = isHorisontal ? getWidth() : getHeight();
super.layoutChildren();
if (inversed) {
double prevEnd = isHorisontal ? offset + getTickLabelGap() : 0;
for (TickMark m : getTickMarks()) {
double position = m.getPosition();
try {
final Text textNode = (Text) textNodeField.get(m);
final Bounds bounds = textNode.getLayoutBounds();
if (0 <= position && position <= offset)
if (isHorisontal) {
textNode.setVisible(position < prevEnd);
prevEnd = position - (bounds.getWidth() + getTickLabelGap());
} else {
textNode.setVisible(position > prevEnd);
prevEnd = position + (bounds.getHeight() + getTickLabelGap());
}
} catch (IllegalAccessException ignored) {
}
}
}
}
答案 2 :(得分:0)
使用轴的属性 lowerBound 和 upperBound 。 lowerBound必须高于upperBound。以下是使用fxml的方法:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<HBox
prefHeight="600.0"
prefWidth="800.0"
xmlns:fx="http://javafx.com/fxml/1">
<javafx.scene.chart.LineChart
fx:id="chart" >
<xAxis >
<javafx.scene.chart.NumberAxis
label="X Axis"
lowerBound="0.01"
upperBound="100"
tickUnit="10"
autoRanging="false" />
</xAxis>
<yAxis>
<javafx.scene.chart.NumberAxis
label="Y Axis"
lowerBound="100"
upperBound="0.001"
tickUnit="10"
autoRanging="false"
/>
</yAxis>
</javafx.scene.chart.LineChart>
</Hbox>
答案 3 :(得分:0)
稍微更新@harshtuna 的回答以避免反射部分。以下代码使用 OpenJFX 16 对我有用
@Override
protected void layoutChildren() {
final Side side = getSide();
boolean isHorizontal = null == side || side.isHorizontal();
double offSetting = isHorizontal ? getWidth() : getHeight();
super.layoutChildren();
if (inversed) {
double prevEnd = isHorizontal ? offSetting + getTickLabelGap() : 0;
for (TickMark m : getTickMarks()) {
double position = m.getPosition();
try {
if (0 <= position && position <= offSetting)
if (isHorizontal) {
m.setTextVisible(position < prevEnd);
prevEnd = position - (2 + getTickLabelGap());
} else {
m.setTextVisible(position > prevEnd);
prevEnd = position + (2 + getTickLabelGap());
}
} catch (Exception ignored) {
System.out.println("illegal...?");
}
}
}
}