Limit width size of Stacked Bar chart - javafx-2

I tried to implement this solution for StackedBar Chart but it turns out that there is no Java method getBarGap() in StackedBar chart. Is there any solution into the latest JavaFX version for this problem?
Basic example:
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MainApp extends Application
{
private StackedBarChart<String, Number> stackedChart;
private List<EventsObj> eventsObj;
#Override
public void start(Stage stage) throws Exception
{
createStackedChart();
List<EventsObj> testData = generateTestData();
addStackedChartData(testData);
HBox hb = new HBox();
hb.getChildren().add(stackedChart);
Scene scene = new Scene(hb);
stage.setTitle("JavaFX and Maven");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
private void createStackedChart()
{
CategoryAxis xAxis = new CategoryAxis();
xAxis.setLabel("Days");
NumberAxis yAxis = new NumberAxis();
stackedChart = new StackedBarChart<>(xAxis, yAxis);
stackedChart.setCategoryGap(20);
stackedChart.widthProperty().addListener((obs, b, b1) ->
{
// Chart Bar column is not automatically resized. We need to wait for next JavaFX releases to fix this.
Platform.runLater(() -> setMaxBarWidth(stackedChart, xAxis, 40, 10));
});
}
private List<EventsObj> generateTestData()
{
eventsObj = new ArrayList<>();
for (int i = 0; i < 5; i++)
{
eventsObj.add(new EventsObj(String.valueOf(randomDate()), random(2, 60), random(2, 60), random(2, 60), random(2, 60)));
}
return eventsObj;
}
public static int random(int lowerBound, int upperBound)
{
return (lowerBound + (int) Math.round(Math.random() * (upperBound - lowerBound)));
}
private LocalDate randomDate()
{
Random random = new Random();
int minDay = (int) LocalDate.of(1900, 1, 1).toEpochDay();
int maxDay = (int) LocalDate.of(2015, 1, 1).toEpochDay();
long randomDay = minDay + random.nextInt(maxDay - minDay);
LocalDate randomBirthDate = LocalDate.ofEpochDay(randomDay);
return randomBirthDate;
}
private void addStackedChartData(List<EventsObj> data)
{
List<XYChart.Series<String, Number>> dataSeries = new ArrayList<>(data.size());
for (EventsObj data1 : data)
{
final XYChart.Series<String, Number> series1 = new XYChart.Series<>();
series1.setName(data1.getDate());
series1.getData().setAll(
new XYChart.Data<>("Info", data1.getInfo()));
dataSeries.add(series1);
}
stackedChart.getData().setAll(dataSeries);
}
private void setMaxBarWidth(StackedBarChart<String, Number> bc, CategoryAxis xAxis, double maxBarWidth, double minCategoryGap)
{
double barWidth = 0;
do
{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getCategoryGap());
barWidth = (avilableBarSpace / bc.getData().size()) - bc.getCategoryGap();
if (barWidth > maxBarWidth)
{
avilableBarSpace = (maxBarWidth + bc.getCategoryGap()) * bc.getData().size();
bc.setCategoryGap(catSpace - avilableBarSpace - bc.getCategoryGap());
}
}
while (barWidth > maxBarWidth);
do
{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (minCategoryGap + bc.getCategoryGap());
barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getCategoryGap());
avilableBarSpace = (barWidth + bc.getCategoryGap()) * bc.getData().size();
bc.setCategoryGap(catSpace - avilableBarSpace - bc.getCategoryGap());
}
while (barWidth < maxBarWidth && bc.getCategoryGap() > minCategoryGap);
}
}

In a BarChart chart, the different series added are plotted in the different columns from the same category with a barGap, some spacing in between.
But in a StackBarChart chart, the different series are plotted stacked in the same column, hence in this case there is no bar gap.
If you take the setMaxBarWidth() method from this answer, all you need to do is set bc.getBarGap() to 0, and take sbc.getData().size() as 1. In this case there is no need for iterations.
This will be the new method, really simplified:
private void setMaxCategoryWidth(double maxCategoryWidth, double minCategoryGap){
double catSpace = xAxis.getCategorySpacing();
sbc.setCategoryGap(catSpace - Math.min(maxCategoryWidth, catSpace - minCategoryGap));
}
Basically, all you have to do is subtract the desired width of your category from the initial width of the category and put this extra space into the categoryGap.
Now, you can create your chart:
StackedBarChart<String, Number> sbc = new StackedBarChart<>(xAxis, yAxis);
And set your desired size for the categories:
setMaxCategoryWidth(40, 10);
sbc.widthProperty().addListener((obs, b, b1) -> {
Platform.runLater(() -> setMaxCategoryWidth(40, 10));
});
This pic is generated based on the sample 7.4 taken from here.

Related

Converting from Timeline to Thread

I am trying to create a program that runs off of a thread instead of a timeline. Here is my modified program down below. I am not sure why it will not work. Any tips would be appreciated. The thread uses a task in order to start the animation. Thanks for your help.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ch30 extends Application {
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
FanPane fan = new FanPane();
HBox hBox = new HBox(5);
Button btPause = new Button("Pause");
Button btResume = new Button("Resume");
Button btReverse = new Button("Reverse");
hBox.setAlignment(Pos.CENTER);
hBox.getChildren().addAll(btPause, btResume, btReverse);
BorderPane pane = new BorderPane();
pane.setCenter(fan);
pane.setBottom(hBox);
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 200, 200);
primaryStage.setTitle("Exercise15_28"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
Runnable first = new Begin();
Thread t1 = new Thread(first);
t1.start();
//Timeline animation = new Timeline(
//new KeyFrame(Duration.millis(100), e -> fan.move()));
//animation.setCycleCount(Timeline.INDEFINITE);
//animation.play(); // Start animation
scene.widthProperty().addListener(e -> fan.setW(fan.getWidth()));
scene.heightProperty().addListener(e -> fan.setH(fan.getHeight()));
//btPause.setOnAction(e -> first.wait());
btResume.setOnAction(e -> first.run());
btReverse.setOnAction(e -> fan.reverse());
}
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
*/
public static void main(String[] args) {
launch(args);
}
}
class FanPane extends Pane {
private double w = 200;
private double h = 200;
private double radius = Math.min(w, h) * 0.45;
private Arc arc[] = new Arc[4];
private double startAngle = 30;
private Circle circle = new Circle(w / 2, h / 2, radius);
public FanPane() {
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
getChildren().add(circle);
for (int i = 0; i < 4; i++) {
arc[i] = new Arc(w / 2, h / 2, radius * 0.9, radius * 0.9, startAngle + i * 90, 35);
arc[i].setFill(Color.RED); // Set fill color
arc[i].setType(ArcType.ROUND);
getChildren().addAll(arc[i]);
}
}
private double increment = 5;
public void reverse() {
increment = -increment;
}
public void move() {
setStartAngle(startAngle + increment);
}
public void setStartAngle(double angle) {
startAngle = angle;
setValues();
}
public void setValues() {
radius = Math.min(w, h) * 0.45;
circle.setRadius(radius);
circle.setCenterX(w / 2);
circle.setCenterY(h / 2);
for (int i = 0; i < 4; i++) {
arc[i].setRadiusX(radius * 0.9);
arc[i].setRadiusY(radius * 0.9);
arc[i].setCenterX(w / 2);
arc[i].setCenterY(h / 2);
arc[i].setStartAngle(startAngle + i * 90);
}
}
public void setW(double w) {
this.w = w;
setValues();
}
public void setH(double h) {
this.h = h;
setValues();
}
}
class Begin implements Runnable {
private int times = 1000;
FanPane fan = new FanPane();
public Begin(){
// times = t;
}
#Override
public void run(){
//for (int i = 0; i < times; i++)
//{
fan.move();
}
}
Your thread needs a loop, that repeatedly pauses and then updates the UI. If you want to use an explicit thread instead of the more obvious Timeline, you need to write the code for that loop yourself.
The update of the UI cannot be called from the background thread you are creating: to update the UI on the FX Application thread you must wrap the call to the method(s) that update the UI in Platform.runLater(...).

JavaFX, apply setStyle to all LineCharts in ObservableList

I have ObservableList<LineChart> backCharts. Also I have class StackChartSeries to compare data's scale. Depending on that I add new backchart with new scale or add series to existed backchart.
Applying style to chart must work with every chart of the specified scale. But it works only with the first chart of the specified scale.
Code next:
public class StackChart extends StackPane {
private LineChart baseChart;
private final ObservableList<LineChart> backCharts = FXCollections.observableArrayList();
private final double yAxisWidth = 60;
private final AnchorPane details;
private final double yAxisSeparation = 20;
private double strokeWidth = 0.5;
private int size = 0;
public StackChart(LineChart baseChart) {
this(baseChart, null);
}
public StackChart(LineChart baseChart, Double strokeWidth) {
if (strokeWidth != null) {
this.strokeWidth = strokeWidth;
}
this.baseChart = baseChart;
baseChart.setCreateSymbols(false);
baseChart.setLegendVisible(false);
baseChart.getXAxis().setAutoRanging(false);
baseChart.getXAxis().setAnimated(false);
baseChart.getXAxis().setStyle("-fx-font-size:" + 18);
baseChart.getYAxis().setAnimated(false);
baseChart.getYAxis().setStyle("-fx-font-size:" + 18);
setFixedAxisWidth(baseChart);
setAlignment(Pos.CENTER_LEFT);
backCharts.addListener((Observable observable) -> rebuildChart());
details = new AnchorPane();
bindMouseEvents(baseChart, this.strokeWidth);
rebuildChart();
}
private void bindMouseEvents(LineChart baseChart, Double strokeWidth) {
getChildren().add(details);
details.prefHeightProperty().bind(heightProperty());
details.prefWidthProperty().bind(widthProperty());
details.setMouseTransparent(true);
setOnMouseMoved(null);
setMouseTransparent(false);
final Axis xAxis = baseChart.getXAxis();
final Axis yAxis = baseChart.getYAxis();
final Line xLine = new Line();
final Line yLine = new Line();
yLine.setFill(Color.GRAY);
xLine.setFill(Color.GRAY);
yLine.setStrokeWidth(strokeWidth/2);
xLine.setStrokeWidth(strokeWidth/2);
xLine.setVisible(false);
yLine.setVisible(false);
final Node chartBackground = baseChart.lookup(".chart-plot-background");
for (Node n: chartBackground.getParent().getChildrenUnmodifiable()) {
if (n != chartBackground && n != xAxis && n != yAxis) {
n.setMouseTransparent(true);
}
}
chartBackground.setCursor(Cursor.CROSSHAIR);
chartBackground.setOnMouseEntered((event) -> {
chartBackground.getOnMouseMoved().handle(event);
xLine.setVisible(true);
yLine.setVisible(true);
details.getChildren().addAll(xLine, yLine);
});
chartBackground.setOnMouseExited((event) -> {
xLine.setVisible(false);
yLine.setVisible(false);
details.getChildren().removeAll(xLine, yLine);
});
chartBackground.setOnMouseMoved(event -> {
double x = event.getX() + chartBackground.getLayoutX();
double y = event.getY() + chartBackground.getLayoutY();
xLine.setStartX(65);
xLine.setEndX(details.getWidth()-10);
xLine.setStartY(y+5);
xLine.setEndY(y+5);
yLine.setStartX(x+5);
yLine.setEndX(x+5);
yLine.setStartY(12);
yLine.setEndY(details.getHeight()-28);
});
}
private void setFixedAxisWidth(LineChart chart) {
chart.getYAxis().setPrefWidth(yAxisWidth);
chart.getYAxis().setMaxWidth(yAxisWidth);
}
private void rebuildChart() {
getChildren().clear();
getChildren().add(resizeBaseChart(baseChart));
for (LineChart lineChart : backCharts) {
getChildren().add(resizeBackgroundChart(lineChart));
}
getChildren().add(details);
}
private Node resizeBaseChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
return lineChart;
}
private Node resizeBackgroundChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
hBox.setMouseTransparent(true);
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.translateXProperty().bind(baseChart.getYAxis().widthProperty());
lineChart.getYAxis().setTranslateX((yAxisWidth + yAxisSeparation) * backCharts.indexOf(lineChart));
return hBox;
}
private LineChart prepareLineChart(String seriesName){
NumberAxis yAxis = new NumberAxis();
NumberAxis xAxis = new NumberAxis();
// xAxis
xAxis.setAutoRanging(false);
xAxis.setVisible(false);
xAxis.setOpacity(0.0);
xAxis.lowerBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).lowerBoundProperty());
xAxis.upperBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).upperBoundProperty());
xAxis.tickUnitProperty().bind(((NumberAxis) baseChart.getXAxis()).tickUnitProperty());
// yAxis
yAxis.setSide(Side.RIGHT);
yAxis.setLabel(seriesName);
// create chart
LineChart lineChart = new LineChart(xAxis, yAxis);
lineChart.setAnimated(false);
lineChart.setLegendVisible(false);
setFixedAxisWidth(lineChart);
return lineChart;
}
public void addSeries(List<StackChartSeries> series){
Collections.sort(series, (x, y) -> (int) (x.getMaxY() - y.getMaxY()));
LineChart lineChart = null;
size = series.size();
for (int i = 0; i < size; i++) {
if (i == 0 || series.get(i).getMaxY() / series.get(i-1).getMaxY() >= 10
|| series.get(i).getMaxY() / series.get(i-1).getMaxY() <= 0.1
|| Math.abs(series.get(i).getMaxY() - series.get(i-1).getMaxY()) >= 900
) {
lineChart = prepareLineChart("Scale" + i);
//new chart with new scale
backCharts.add(lineChart);
}
lineChart.getData().add(series.get(i).getSeries());
}
int j = 0;
for (LineChart chart : backCharts) {
styleBackChart(chart);
//HERE
setLineStyle(chart, j+3);
}
}
private void styleBackChart(LineChart lineChart) {
setStyle(lineChart);
Node contentBackground = lineChart.lookup(".chart-content").lookup(".chart-plot-background");
contentBackground.setStyle("-fx-background-color: transparent;");//
lineChart.setVerticalZeroLineVisible(false);
lineChart.setHorizontalZeroLineVisible(false);
lineChart.setVerticalGridLinesVisible(false);
lineChart.setHorizontalGridLinesVisible(false);
lineChart.setCreateSymbols(false);
lineChart.getXAxis().setStyle("-fx-font-size:" + 18);
lineChart.getYAxis().setStyle("-fx-font-size:" + 18);
}
private void setStyle(LineChart chart) {
chart.getYAxis().lookup(".axis-label").setStyle("-fx-font-size: 24;");
Node seriesLine = chart.lookup(".chart-series-line");
seriesLine.setStyle("-fx-stroke-width: " + strokeWidth + ";");
}
public Node getLegend() {
HBox hbox = new HBox();
final CheckBox baseChartCheckBox = new CheckBox(baseChart.getYAxis().getLabel());
baseChartCheckBox.setSelected(true);
baseChartCheckBox.setDisable(true);
baseChartCheckBox.getStyleClass().add("readonly-checkbox");
baseChartCheckBox.setOnAction(event -> baseChartCheckBox.setSelected(true));
hbox.getChildren().add(baseChartCheckBox);
for (final LineChart lineChart : backCharts) {
CheckBox checkBox = new CheckBox(lineChart.getYAxis().getLabel());
checkBox.setSelected(true);
checkBox.setOnAction(event -> {
if (backCharts.contains(lineChart)) {
backCharts.remove(lineChart);
} else {
backCharts.add(lineChart);
}
});
hbox.getChildren().add(checkBox);
}
hbox.setAlignment(Pos.CENTER);
hbox.setSpacing(20);
hbox.setStyle("-fx-padding: 0 10 20 10");
return hbox;
}
private void setLineStyle (LineChart chart, Integer k) {
Node seriesLine = chart.lookup(".chart-series-line");
seriesLine.setStyle(" -fx-stroke-dash-array: " + k + " 5 15 5;");
}
}
StackChartSeries.java:
import javafx.scene.chart.XYChart;
import org.apache.commons.lang.ArrayUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
public class StackChartSeries {
private XYChart.Series series;
private final double[] xValues;
private final double[] yValues;
private Optional<Double> maxY;
public StackChartSeries(double[] xValues, double[] yValues) {
this.xValues = xValues;
this.yValues = yValues;
}
public XYChart.Series getSeries() {
return series != null ? series : (series = prepareSeries(xValues, yValues));
}
private static XYChart.Series<Number, Number> prepareSeries( double[] xValues, double[] yValues) {
XYChart.Series<Number, Number> series = new XYChart.Series<>();
for (int i = 0; i < yValues.length; i++){
series.getData().add(new XYChart.Data(xValues[i], yValues[i]));
}
return series;
}
public double getMaxY(){
if (maxY != null){
return maxY.get();
}
else {
Double d = C?ollections.max(Arrays.asList(ArrayUtils.toObject(yValues)));
maxY = Optional.of(d);
return maxY.get();
}
}
public double[] getXValues() {
return xValues;
}
public double[] getYValues() {
return yValues;
}
}

BarChart Update

Now it is another problem that occurred, it seems that the query I'm using only works for the first time but after pressing the next/previous buttons, it is giving me something else !!
Here is the query I used:
for (int i = 0; i <= months.length + 1; i++) {
try {
String a;
if (i < 9) {
a = y + "0" + (i + 1);
} else {
a = y + "" + (i + 1);
}
System.out.println("Année Courante " + a);
conn = DBConnection.connect();
String sql = "select sum(montant_operation) from operations where (select Extract(YEAR_MONTH from date_operation)) = '" + a + "' and typ_operation ='Versement';";
final ResultSet rs = conn.prepareStatement(sql).executeQuery();
if (rs.next()) {
System.out.println(series1.getData().toString());
series1.getData().add(new XYChart.Data<>(months[i], rs.getFloat("sum(montant_operation)")));
}
} catch (SQLException e) {
System.out.println(e);
}
}
But is there a query that works fine one time and then it gives error.
Have a nice day
In your BuildData (by the way, not following Java Naming Conventions, change its name) method you are updating the data of series. In the same method you are adding this series to the chart. By clicking the "next" button, BuildData method is invoked where this chart is added again which is unnecessary. Delete the
Platform.runLater(() -> {
barchart.getData().add(series1);
});
part from the method and add the chart only once in start:
...
...
vbox.getChildren().addAll(box, barchart);
barchart.getData().add(series1);
pane.getChildren().add(vbox);
...
...
The tested SSCCE:
import java.util.Calendar;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BarChartDemo extends Application {
final String[] months = {"Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"};
//Connection conn;
final CategoryAxis month_axis = new CategoryAxis();
final NumberAxis data_axis = new NumberAxis();
final XYChart.Series<String, Number> series1 = new XYChart.Series();
private final BarChart<String, Number> barchart = new BarChart(month_axis, data_axis);
private Integer year = 0;
#Override
public void start(Stage primaryStage) {
year = Calendar.getInstance().get(Calendar.YEAR);
Button btn_next = new Button("NEXT");
Button btn_previous = new Button("PREVIOUS");
HBox box = new HBox(50);
box.getChildren().addAll(btn_previous, btn_next);
box.setAlignment(Pos.TOP_CENTER);
VBox vbox = new VBox(25);
box.setPadding(new Insets(10, 0, 10, 0));
FlowPane pane = new FlowPane(Orientation.VERTICAL);
vbox.getChildren().addAll(box, barchart);
barchart.getData().add(series1);
pane.getChildren().add(vbox);
Scene scene = new Scene(pane);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
BuildData(year);
btn_next.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
year += 1;
BuildData(year);
}
});
btn_previous.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
year -= 1;
BuildData(year);
}
});
}
public static void main(String[] args) {
launch(args);
}
private void BuildData(Integer y) {
series1.setName("Versement");
month_axis.setLabel("Mois de l'Année");
month_axis.setStyle("-fx-font-weight:BOLD;" + "-fx-font-size:15");
data_axis.setLabel("Valeur des Opérations Bancaires");
data_axis.setStyle("-fx-font-weight:BOLD;" + "-fx-font-size:15");
series1.getData().clear(); // clear old values
for (int i = 0; i < months.length; i++) {
series1.getData().add(new XYChart.Data(months[i], i * 10 * (y-2000)));
// try {
// String a;
// if (i < 9) {
// a = y + "0" + (i + 1);
// } else {
// a = y + "" + (i + 1);
// }
// conn = DBConnection.connect();
// String sql = "select sum(montant_operation) from operations where (select Extract(YEAR_MONTH from date_operation)) = '" + a + "' and typ_operation ='Versement';";
// final ResultSet rs = conn.prepareStatement(sql).executeQuery();
// if (rs.next()) {
// series1.getData().add(new XYChart.Data<>(months[i], rs.getFloat("sum(montant_operation)")));
// }
// } catch (SQLException e) {
// System.out.println(e);
// }
}
}
}

How to draw multiline Edittext in notepad?

I am creating a notepad and I want to have edit text with multiple horizontal lines on it. As I have done with some but It is displaying line when I click on next or enter to the next line. I want that lines already be there. When I am using layour with android:lines="5". It is displaying picture as I have attached.
Please suggest on the same. thanks
EDITED: My EditText is showing like this!!!! A Huge gap on top. Please suggest what to do?
This is the code, based on google's note editorThe output will be shown in the image.When you press the enter,new lines will be added.
public class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(R.color.edit_note_line); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
//int count = getLineCount();
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();//for long text with scrolling
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);//first line
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
super.onDraw(canvas);
}
}
For more info,Refer this link.
Simple add this line in your XML
android:gravity="top|left"
Here , my code will draw 15 lines by default and you can get more lines by pressing Enter :-
package com.wysiwyg.main;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.EditText;
public class LineEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LineEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.BLUE); //SET YOUR OWN COLOR HERE
setMinLines(15);
}
#Override
protected void onDraw(Canvas canvas) {
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if(getLineCount() > count){
count = getLineCount();
}
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
// Finishes up by calling the parent method
super.onDraw(canvas);
}
}

Retrieve String values from a CategoryAxis in a javaFX Line Chart -CategoryAxis JavaFx 2.2.3 bug in .getValueForDisplay(double displayPosition)-

I have a JavaFXline chart with months versus values. I'm trying to print in the console the month and the value from a Line chart every time I click the graph .The problem is that when I call the method getValueForDisplay in my CategoryAxis object, it returns null.
When I click (x,y):jan,3
I want to print:
X value = jan Y value: 3
but the program is printing:
X value = **null** Y value: 2.7796610169491527
Another question;
2) Why when attempting to print the coordinates it seems to be not calibrated (if I click on 3 the program print 2.779661016949152? You can notice this issue checking the values printed on the console.
Here is the code I'm using:
public class LineChartTest extends Application {
String xValue;
Number yValue;
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis("Values for Y-Axis", 0, 3, 1);
yAxis.setUpperBound(5);
ObservableList<XYChart.Series<String,Double>> lineChartData = FXCollections.observableArrayList(
new LineChart.Series<String,Double>("en", FXCollections.observableArrayList(
new XYChart.Data<String,Double>("jan", 1.0),
new XYChart.Data<String,Double>("feb", 2.0),
new XYChart.Data<String,Double>("mar", 3.0),
new XYChart.Data<String,Double>("apr", 4.0),
new XYChart.Data<String,Double>("may", 0.5)
)),
new LineChart.Series<String,Double>("to", FXCollections.observableArrayList(
new XYChart.Data<String,Double>("jan", 1.6),
new XYChart.Data<String,Double>("feb", 0.4),
new XYChart.Data<String,Double>("mar", 2.9),
new XYChart.Data<String,Double>("apr", 1.3),
new XYChart.Data<String,Double>("may", 0.9)
))
);
//define a new line
final Series<String,Double> otherSeries = new Series<String,Double>();
//set points
otherSeries.getData().add(new XYChart.Data("jan",3));
otherSeries.getData().add(new XYChart.Data("feb",2));
otherSeries.getData().add(new XYChart.Data("mar",4));
//set name to the line
otherSeries.setName("tre");
//add the new line to the graph
lineChartData.add(otherSeries);
final LineChart chart = new LineChart(xAxis, yAxis, lineChartData);
//set title to the figure
chart.setTitle("Line Chart Test");
root.getChildren().add(chart);
chart.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
//Why this seems to be not working properly?
double sceneX = t.getSceneX();
double sceneY = t.getSceneY();
//gives the point of the graph
xValue = xAxis.getValueForDisplay(sceneX);
yValue = yAxis.getValueForDisplay(sceneY);
System.out.println("sceneX-25: "+ sceneX);
System.out.println("getDisplayPosition: " + xAxis.getValueForDisplay(sceneX));
//Test
System.out.println(" X value = " +
xValue + " Y value: " + yValue );
}
});
}
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Answer:
Thanks to Sergey Grinev and Uluk Biy
in the previous code you have to modified:
Line chart code:
chart.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
Node chartPlotBackground = chart.lookup(".chart-plot-background");
final double shiftX = xSceneShift(chartPlotBackground);
final double shiftY = ySceneShift(chartPlotBackground);
double x = e.getSceneX() - shiftX;
double y = e.getSceneY() - shiftY;
xValue = xAxis.getValueForDisplay(x);
yValue = yAxis.getValueForDisplay(y );
System.out.println("shiftX = " + shiftX + " shiftY: " + shiftY);
System.out.println("X value = "
+ xValue + " \nY value: " + yValue);
}
});
}
//recursive calls
private double xSceneShift(Node node) {
return node.getParent() == null ? 0 : node.getBoundsInParent().getMinX() + xSceneShift(node.getParent());
}
private double ySceneShift(Node node) {
return node.getParent() == null ? 0 : node.getBoundsInParent().getMinY() + ySceneShift(node.getParent());
}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
in the CategoryAxis source code you have to modified (look at the comments!):
#Override public String getValueForDisplay(double displayPosition) {
if (getSide().equals(Side.TOP) || getSide().equals(Side.BOTTOM)) { // HORIZONTAL
if (displayPosition < 0 || displayPosition > getHeight()) return null; // <-------------- WRONG SHOULD BE displayPosition > getWidth()
double d = (displayPosition - firstCategoryPos.get()) / categorySpacing.get();
return toRealValue(d);
} else { // VERTICAL
if (displayPosition < 0 || displayPosition > getWidth()) return null; // <-------------- WRONG SHOULD BE displayPosition > getHeight()
double d = (displayPosition - firstCategoryPos.get()) / (categorySpacing.get() * -1);
return toRealValue(d);
}
}
you can find the source code here:
http://hg.openjdk.java.net/openjfx/2.1/master/rt/file/5c3b3d524f07/javafx-ui-controls/src/javafx/scene/chart/CategoryAxis.java
If you are aiming to display the data point that user clicked on the chart, then you can access directly to the underlining data. Hover to the data X-Y intersection points on this example:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.LabelBuilder;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
public class ChartDemo extends Application {
private DropShadow ds = new DropShadow();
#Override
public void start(Stage stage) {
stage.setTitle("Linear plot");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(0, 22, 0.5);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.2f", object);
}
});
final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(true);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
final XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 1.5));
series1.getData().add(new XYChart.Data("Mar", 2));
series1.getData().add(new XYChart.Data("Apr", 2.5));
series1.getData().add(new XYChart.Data("May", 3));
series1.getData().add(new XYChart.Data("Jun", 4));
series1.getData().add(new XYChart.Data("Jul", 6));
series1.getData().add(new XYChart.Data("Aug", 9));
series1.getData().add(new XYChart.Data("Sep", 12));
series1.getData().add(new XYChart.Data("Oct", 15));
series1.getData().add(new XYChart.Data("Nov", 20));
series1.getData().add(new XYChart.Data("Dec", 22));
final Label lbl = LabelBuilder.create().font(Font.font("Arial", FontWeight.BOLD, 25))
.textFill(Color.BLUEVIOLET).translateY(-200).build();
StackPane pane = new StackPane();
pane.getChildren().addAll(lineChart, lbl);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
for (Object obj : series1.getData()) {
final XYChart.Data data = (XYChart.Data) obj;
final Node node = data.getNode();
node.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
node.setEffect(ds);
node.setCursor(Cursor.HAND);
lbl.setText("X-value=" + data.getXValue().toString() + "\nY-value=" + data.getYValue().toString());
System.out.println("X-value=" + data.getXValue().toString() + ", Y-value=" + data.getYValue().toString());
}
});
node.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
node.setEffect(null);
node.setCursor(Cursor.DEFAULT);
lbl.setText("");
}
});
}
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
There are 2 issues.
1) You are using wrong coordinates. getValueForDisplay expects coordinates relative to chart drawing area, not to scene.
To fix that you can use next trick.
Introduce next recursive calls to calculate scene shift:
private double xSceneShift(Node node) {
return node.getParent() == null ? 0 : node.getBoundsInParent().getMinX() + xSceneShift(node.getParent());
}
private double ySceneShift(Node node) {
return node.getParent() == null ? 0 : node.getBoundsInParent().getMinY() + ySceneShift(node.getParent());
}
Find shift of chart drawing area:
Node chartPlotBackground = chart.lookup(".chart-plot-background");
final double shiftX = xSceneShift(chartPlotBackground);
final double shiftY = ySceneShift(chartPlotBackground);
And use them in your mouse handler:
public void handle(MouseEvent e) {
double x = e.getSceneX() - shiftX;
double y = e.getSceneY() - shiftY;
xValue = xAxis.getValueForDisplay(x );
yValue = yAxis.getValueForDisplay(y);
System.out.println(" X value = "
+ xValue + " \nY value: " + yValue);
}
2) You met an issue in CategoryAxis which is fixed but in next release (8.0): http://javafx-jira.kenai.com/browse/RT-25899
As a workaround you can download CategoryAxis source, rename it to MyCategoryAxis and fix a bug in method getValueForDisplay() -- just switch places of getWidth() and getHeight() call.
http://hg.openjdk.java.net/openjfx/2.1/master/rt/file/5c3b3d524f07/javafx-ui-controls/src/javafx/scene/chart/CategoryAxis.java

Resources