Loan calculator using tableview in JavaFX - javafx

I am attempting to make a loan calculator that shows payment number, interest, principal, and balance in tableview in JavaFX. Right now only the principal and balance columns are populating. I am fairly new to JavaFX, so I am not sure if I have made a mistake in FX or in my formula. Here is my code:
public class LoanTable extends Application {
Integer payment;
Double interest;
Double principal;
Double balance;
#Override
public void start(Stage primaryStage) {
BorderPane border = new BorderPane();
GridPane grid = new GridPane();
ScrollPane scroll = new ScrollPane();
Label labelTitle = new Label("Enter Loan Amount, Number of Years, and Annual Interest Rate");
labelTitle.getStyleClass().add("labelTitle");
Label loan = new Label("Loan Amount");
TextField loanAmount = new TextField();
Label years = new Label("Number of Years");
TextField numYears = new TextField();
Label interestRate = new Label("Annual Interest Rate");
TextField rate = new TextField();
Button compute = new Button("Display Loan Schedule");
TableView<Finance> table = new TableView<>();
TableColumn paymentColumn = new TableColumn("Payment#");
paymentColumn.setMinWidth(200);
paymentColumn.setCellValueFactory(new PropertyValueFactory<Finance, Integer>("payment"));
TableColumn interestColumn = new TableColumn("Interest");
interestColumn.setMinWidth(200);
interestColumn.setCellValueFactory(new PropertyValueFactory<Finance, Double>("interest"));
TableColumn prinColumn = new TableColumn("Principal");
prinColumn.setMinWidth(200);
prinColumn.setCellValueFactory(new PropertyValueFactory<Finance, Double>("principal"));
TableColumn balColumn = new TableColumn("Balance");
balColumn.setMinWidth(200);
balColumn.setCellValueFactory(new PropertyValueFactory<Finance, Double>("balance"));
table.getColumns().addAll(paymentColumn, interestColumn, prinColumn, balColumn);
grid.add(labelTitle, 0, 0);
grid.add(new Label(), 0, 1);
grid.add(loan, 0, 2);
grid.add(loanAmount, 1, 2);
grid.add(years, 0, 3);
grid.add(numYears, 1, 3);
grid.add(interestRate, 0, 4);
grid.add(rate, 1, 4);
grid.add(compute, 4, 0);
scroll.setContent(table);
border.setTop(grid);
border.setBottom(scroll);
Scene scene = new Scene(border, 800, 800);
scene.getStylesheets().add("loan_table.css");
primaryStage.setScene(scene);
primaryStage.show();
compute.setOnAction((e) -> {
Double loanTotal = Double.parseDouble(loanAmount.getText());
Integer year = Integer.parseInt(numYears.getText());
Double rateInterest = Double.parseDouble(rate.getText());
double monthlyInterest = rateInterest / 1200;
double monthlyPayment = loanTotal * monthlyInterest / (1 - 1 / Math.pow(1 + monthlyInterest, year * 12));
balance = loanTotal;
for(int i = 1; i < year * 12; i++) {
interest = monthlyInterest * balance;
principal = monthlyPayment - interest;
balance = balance - principal;
payment = i + 1;
}
ObservableList<Finance> data = FXCollections.observableArrayList(new Finance(payment, interest, principal, balance));
table.setItems(data);
});
}
public static class Finance {
private final SimpleIntegerProperty payment;
private final SimpleDoubleProperty interest;
private final SimpleDoubleProperty principal;
private final SimpleDoubleProperty balance;
private Finance(int payment, double interest, double principal, double balance) {
this.payment = new SimpleIntegerProperty(payment);
this.interest = new SimpleDoubleProperty(interest);
this.principal = new SimpleDoubleProperty(principal);
this.balance = new SimpleDoubleProperty(balance);
}
public Integer getPayment(int numYears) {
return payment.get();
}
public Double getInterest(double rate) {
return interest.get();
}
public Double getPrincipal() {
return principal.get();
}
public Double getBalance() {
return balance.get();
}
}
public static void main(String[] args) {
launch(args);
}
}

Related

JavaFX plotting LinexChart

I am trying to learn how plot graph using JavaFX and there is my code where I trying to draw the very simple sin series:
public class Controller implements Initializable {
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
#FXML
private LineChart<Number, Number> ExactChart = new LineChart<Number, Number>(xAxis, yAxis);
private Series sin_series = new Series();
#FXML
private void plotTheChart(ActionEvent event){
int N = 100;
double x0 = -Math.PI;
double X = Math.PI;
double h = (X-x0)/(N);
double[] x = new double[N];
double[] y = new double[N];
x[0] = x0;
for(int i = 1; i < N; i++){
x[i] = x[i-1] + h;
}
for(int i = 0; i < N; i++){
y[i] = Math.sin(x[i]);
}
sin_series.setName("sin");
for(int i = 0; i < N; i++){
sin_series.getData().add(new Data(x[i], y[i]));
}
ExactChart.getData().addAll(sin_series);
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
}
But when I execute it I face following problem:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.String (java.lang.Double and java.lang.String are in module java.base of loader 'bootstrap')
I do not see any part of the code where I am castin double to string. Can you show me where I am wrong?

How to mix ScatterChart with horizontal and vertical lines

I'm trying to create vertical and horizontal lines on ScatterChart together with points. I cannot find a way to mix them up.
This is the code I generate points on chart.
vbox {
add(ScatterChart(NumberAxis(), NumberAxis()).apply {
val seriesMap: HashMap<String, XYChart.Series<Number, Number>> = HashMap()
pointsList
.map { it.decisionClass }
.distinct()
.forEach {
seriesMap.put(it, XYChart.Series())
}
for (point in pointsList) {
seriesMap.get(point.decisionClass)?.data(point.axisesValues[0], point.axisesValues[1])
}
seriesMap
.toSortedMap()
.forEach { key, value ->
value.name = key
data.add(value)
}
(xAxis as NumberAxis).setForceZeroInRange(false)
(yAxis as NumberAxis).setForceZeroInRange(false)
})
}
I don't know Kotlin, so this answer is in Java. I think you can probably translate it to Kotlin (and feel free to post another answer if so).
To add additional nodes to a chart, there are three things you need:
Call getPlotChildren() and add the new nodes to the chart's "plot children"
Override the layoutPlotChildren() method to update the positions of your nodes when the chart is laid out
Use getDisplayPosition(...), defined in Axis, to get the location in the coordinate system of the plot area of the chart from a value on the axis.
The following SSCCE creates a scatter chart somewhat similar to the one you posted in the screen shot, and adds lines gating a specified series (i.e. the line on the left extends the height of the chart and passes through the minimum x-value of the series; the line at the top extends the width of the chart, and passes through the maximum y-value of the series, etc). I added radio buttons so you can choose which series is "bounded" by the lines.
import java.util.Random;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class ScatterChartWithLines extends Application {
private final class ScatterChartWithBoundary extends ScatterChart<Number, Number> {
private Series<Number, Number> boundedSeries ;
private final NumberAxis yAxis;
private final NumberAxis xAxis;
private final Line leftLine = new Line();
private final Line rightLine = new Line();
private final Line topLine = new Line();
private final Line bottomLine = new Line();
{
getPlotChildren().addAll(leftLine, rightLine, topLine, bottomLine);
}
private ScatterChartWithBoundary(NumberAxis xAxis, NumberAxis yAxis) {
super(xAxis, yAxis);
this.yAxis = yAxis;
this.xAxis = xAxis;
}
#Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
getPlotChildren().removeAll(leftLine, rightLine, topLine, bottomLine);
if (boundedSeries != null) {
getPlotChildren().addAll(leftLine, rightLine, topLine, bottomLine);
double minX = Double.MAX_VALUE ;
double minY = Double.MAX_VALUE ;
double maxX = Double.MIN_VALUE ;
double maxY = Double.MIN_VALUE ;
for (Data<Number, Number> d : boundedSeries.getData()) {
if (d.getXValue().doubleValue() < minX) minX = d.getXValue().doubleValue() ;
if (d.getXValue().doubleValue() > maxX) maxX = d.getXValue().doubleValue() ;
if (d.getYValue().doubleValue() < minY) minY = d.getYValue().doubleValue() ;
if (d.getYValue().doubleValue() > maxY) maxY = d.getYValue().doubleValue() ;
}
positionLineInAxisCoordinates(leftLine, minX, yAxis.getLowerBound(), minX, yAxis.getUpperBound());
positionLineInAxisCoordinates(rightLine, maxX, yAxis.getLowerBound(), maxX, yAxis.getUpperBound());
positionLineInAxisCoordinates(bottomLine, xAxis.getLowerBound(), minY, xAxis.getUpperBound(), minY);
positionLineInAxisCoordinates(topLine, xAxis.getLowerBound(), maxY, xAxis.getUpperBound(), maxY);
}
}
private void positionLineInAxisCoordinates(Line line, double startX, double startY, double endX, double endY) {
double x0 = xAxis.getDisplayPosition(startX);
double x1 = xAxis.getDisplayPosition(endX);
double y0 = yAxis.getDisplayPosition(startY);
double y1 = yAxis.getDisplayPosition(endY);
line.setStartX(x0);
line.setStartY(y0);
line.setEndX(x1);
line.setEndY(y1);
}
public void setBoundedSeries(Series<Number, Number> boundedSeries) {
if (! getData().contains(boundedSeries)) {
throw new IllegalArgumentException("Specified series is not displayed in this chart");
}
this.boundedSeries = boundedSeries ;
requestChartLayout();
}
}
private final Random rng = new Random();
#Override
public void start(Stage primaryStage) {
Series<Number, Number> series1 = new Series<>("Series 1", FXCollections.observableArrayList());
Series<Number, Number> series2 = new Series<>("Series 2", FXCollections.observableArrayList());
Series<Number, Number> series3 = new Series<>("Series 3", FXCollections.observableArrayList());
for (int i = 0 ; i < 40 ; i++) {
series1.getData().add(new Data<>(rng.nextDouble()*2 + 4, rng.nextDouble()*3 + 2));
series2.getData().add(new Data<>(rng.nextDouble()*2.5 + 4.75, rng.nextDouble()*1.5 + 2));
series3.getData().add(new Data<>(rng.nextDouble()*3 + 5, rng.nextDouble()*1.5 + 2.75));
}
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
xAxis.setForceZeroInRange(false);
yAxis.setForceZeroInRange(false);
ScatterChartWithBoundary chart = new ScatterChartWithBoundary(xAxis, yAxis);
chart.getData().addAll(series1, series2, series3);
VBox buttons = new VBox(2);
ToggleGroup toggleGroup = new ToggleGroup();
for (Series<Number, Number> series : chart.getData()) {
RadioButton rb = new RadioButton(series.getName());
rb.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
chart.setBoundedSeries(series);
}
});
rb.setToggleGroup(toggleGroup);
buttons.getChildren().add(rb);
}
BorderPane root = new BorderPane(chart);
root.setTop(buttons);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is a typical result:

Synchronization in Javafx

I'm trying to make a method that returns after the user clicks on a rectangle in the stage. However, when calling wait() and then later notify() when the rectangle is clicked, the program freezes. How would one make a showAndWait() method in javafx?
I'm trying to make a class that asks a multiple choice question then returns the result, the below code is this class and it's commented where I attempted to put a wait and notify call.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
public class Question{
int answer; //refers to the answer in the order of strings given
// a = 1, b = 2, c = 3, d = 4
String question;
String a,b,c,d;
public Question(String question, String a, String b, String c, String d, int answer){
this.question = question;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.answer = answer;
}
private final double sideLength = 500;
private volatile byte complete; //0 = no, 1 = true, -1 = false
public synchronized boolean showAndWait(){
complete = 0;
Stage stage = new Stage();
Pane pane = new Pane();
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.setResizable(false);
stage.setMaxHeight(sideLength);
stage.setMinHeight(sideLength);
stage.setMaxWidth(sideLength);
stage.setMinWidth(sideLength);
double width = sideLength/3;
double height = sideLength/5;
double firstX = sideLength/9;
double secondX = width + sideLength/9*2;
double firstY = height + sideLength*2/5/3;
double secondY = firstY * 2;
Color base = Color.AQUAMARINE;
Color wrong = Color.RED;
Color right = Color.GREEN;
Pane questionPane = makeAnswer(0,0,sideLength,sideLength/5, question, base, base);
Pane ansA = makeAnswer(firstX, firstY, width, height,a,base, wrong);
Pane ansB = makeAnswer(secondX, firstY, width, height,b,base, wrong);
Pane ansC = makeAnswer(firstX, secondY, width, height,c,base, wrong);
Pane ansD = makeAnswer(secondX, secondY, width, height,d,base, wrong);
pane.getChildren().addAll(questionPane, ansA, ansB, ansC, ansD);
stage.show();
//wait here, check to see if completed
System.out.println(complete);
return complete == 1;
}
private double textShiftX = 30;
private double textShiftY = 30;
private int characterLimit = 20;
private double textChangeY = 10;
private synchronized Pane makeAnswer(double x, double y,double width, double height, String s, Color base, Color afterClick){
Pane temp = new Pane();
temp.setLayoutX(x);
temp.setLayoutY(y);
Rectangle rD = new Rectangle(0, 0, width, height);
rD.setFill(base);
temp.getChildren().add(rD);
for(int i=0;s.length() > 0;i++){
String sTemp;
if(s.length()>characterLimit){
sTemp = s.substring(0,characterLimit);
while(sTemp.charAt(sTemp.length()-1) != ' ')
sTemp = sTemp.substring(0,sTemp.length()-1);
}
else
sTemp = s;
s = s.substring(sTemp.length());
Text t = new Text(textShiftX, textShiftY + textChangeY*i, sTemp);
temp.getChildren().add(t);
}
rD.setOnMouseClicked(
(e)->{
((Rectangle)e.getSource()).setFill(afterClick);
complete = 1;
//notify
});
return temp;
}

BlackJack javafx hit button doesn't work

I'm having trouble getting the Hit button to work in my BlackJack program. I tried programming the action for the hit button in the setOnAction block as well as lambda, but I get an error about the variables not being final. So, I tried it this way, but I don't think my variables are carrying over. It's probably something very simple. Please help if you can. Thanks!
import java.util.Arrays;
import java.util.ArrayList;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Labeled;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
public class BlackJackGame extends Application {
public String btHitY;
public int NumberPlayerCards;
public int NumberDealerCards;
public int NUMBER_OF_CARDS;
public int PlayerCards[];
public int DealerCards[];
public Image imagesP[];
public Image imagesD[];
public int deck[];
public String URLBase;
public GridPane pane;
public BlackJackGame() {
this.btHitY = " ";
this.btHitY = new String();
}
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//Create array deck, suit string, and rank string
int[] deck = new int[52];
String[] suits = {"Spades", "Hearts", "Diamonds", "Clubs"};
String[] ranks = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"};
int NUMBER_OF_CARDS = 4;
String URLBase = "http://www.cs.armstrong.edu/liang/common/image/card/";
//Initialize the cards
for (int i = 0; i < deck.length; i++)
deck[i] = i;
//Shuffle the cards
for (int i = 0; i < deck.length; i++) {
//Generate an index randomly
int index = (int)(Math.random() * deck.length);
int temp = deck[i];
deck[i] = deck[index];
deck[index] = temp;
}
int NumberPlayerCards = 2;
int NumberDealerCards = 2;
int[] PlayerCards = new int[50];
int[] DealerCards = new int[50];
//Display the first cards
for (int i = 0; i < 8; i++) {
String suit = suits[deck[i] / 13];
String rank = ranks[deck[i] % 13];
System.out.println("Card number " + deck[i] + ": " + rank + " of " + suit);
}
for (int i = 0; i < NumberPlayerCards; i++)
PlayerCards[i] = deck[i * 2];
for (int i = 0; i < NumberDealerCards; i++)
DealerCards[i] = deck[i * 2 + 1];
// Create a pane to hold the image views
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setPadding(new Insets(5, 5, 5, 5));
pane.setHgap(5);
pane.setVgap(5);
Image[] imagesP = new Image[50];
Image[] imagesD = new Image[50];
for (int i = 0; i < NumberPlayerCards; i++) {
int cardForPrint = PlayerCards[i] + 1;
System.out.println(URLBase + cardForPrint + ".png");
imagesP[i] = new Image(URLBase + cardForPrint + ".png");
}
for (int i = 0; i < NumberDealerCards; i++) {
int cardForPrint = DealerCards[i] + 1;
System.out.println(URLBase + cardForPrint + ".png");
imagesD[i] = new Image(URLBase + cardForPrint + ".png");
}
//rotate flag image to cover dealer card
Image flag = new Image("http://www.cs.armstrong.edu/liang/common/image/us.gif");
ImageView imageFlag = new ImageView(flag);
imageFlag.setRotate(90);
imageFlag.setFitHeight(75);
imageFlag.setFitWidth(95);
pane.add(new Label("Player Cards"), 0, 0);
pane.add(new ImageView(imagesP[0]), 1, 0);
pane.add((imageFlag), 1, 1);
pane.add(new Label("Dealer Cards"), 0, 1);
pane.add(new ImageView(imagesP[1]), 2, 0);
pane.add(new ImageView(imagesD[1]), 2, 1);
Button btHit = new Button("Hit");
Button btStay = new Button("Stay");
pane.add(btHit, 1, 2);
pane.add(btStay, 2, 2);
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 1200, 700);
primaryStage.setTitle("Black Jack"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
HitHandlerClass handlerHit = new HitHandlerClass();
btHitY = " ";
btHit.setOnAction(handlerHit);
/* if (btHitY.equals("Hit")); {
NumberPlayerCards = NumberPlayerCards + 1;
NUMBER_OF_CARDS = NUMBER_OF_CARDS + 1;
PlayerCards[NumberPlayerCards - 1] = deck[NUMBER_OF_CARDS - 1];
for (int j = 0; j < NumberPlayerCards; j++){
System.out.println(PlayerCards[j]);
}
System.out.println(NumberPlayerCards);
int CardForPrint2 = PlayerCards[NumberPlayerCards - 1] + 1;
imagesP[NumberPlayerCards - 1] = new Image(URLBase + CardForPrint2 + ".png");
pane.add(new ImageView(imagesP[NumberPlayerCards - 1]), NumberPlayerCards, 0);
btHitY = " ";
primaryStage.show();
} */
}
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
* #param args
*/
public static void main(String[] args) {
launch(args);
}
class HitHandlerClass implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent e) {
NumberPlayerCards = NumberPlayerCards + 1;
NUMBER_OF_CARDS = NUMBER_OF_CARDS + 1;
PlayerCards[NumberPlayerCards - 1] = deck[NUMBER_OF_CARDS - 1];
for (int j = 0; j < NumberPlayerCards; j++){
System.out.println(PlayerCards[j]);
}
System.out.println(NumberPlayerCards);
int CardForPrint2 = PlayerCards[NumberPlayerCards - 1] + 1;
imagesP[NumberPlayerCards - 1] = new Image(URLBase + CardForPrint2 + ".png");
pane.add(new ImageView(imagesP[NumberPlayerCards - 1]), NumberPlayerCards, 0);
btHitY = " ";
}
}
}
You declare this global
public int deck[];
and then local
int[] deck = new int[52];
So the global never gets initialized and gives you a Nullpointer Exception.
Solution:
deck = new int[52];
Same for the other variables.
public class BlackJackGame extends Application {
//snip
public int NUMBER_OF_CARDS;
public int deck[];
//snip
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//Create array deck, suit string, and rank string
int[] deck = new int[52];
int NUMBER_OF_CARDS = 4;
//snip
You've declared deck[] and NUMBER_OF_CARDS at a higher scope, then redeclared them later, at a lower scope.
To fix these issues, simply remove the type declaration in the start method.
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//Create array deck, suit string, and rank string
deck = new int[52];
NUMBER_OF_CARDS = 4;
//snip
As a side note, I would like to recommend you read up a little bit on Code conventions for Java/Javafx, as they help other programmers read your code by providing a standard style.

Put XAxis value on top of bar chart with diagonally in javafx

I have created a bar chart with multi series as you can see at the picture.
The List ent has 2 Lists.
I need to somehow write their xAxis value on top of their body diagonally (with a rotation of 45 degree). And set another value for xAxis later.
public class FunctionalRedundantController {
#FXML
private BarChart<String, Number> barchart;
#FXML
private CategoryAxis funcNameAxis;
#FXML
private NumberAxis yAxis;
private ObservableList<String> funcNames = FXCollections
.observableArrayList();
private DataConstructor dc = new DataConstructor();
#FXML
private void initialize() {
funcNameAxis.setLabel("Name of Functions");
funcNameAxis.tickLabelFontProperty().set(Font.font(10));
yAxis.setLabel("Redundant");
}
public void setfunctionalredundant() {
XYChart.Series<String, Number> series = new XYChart.Series<>();
XYChart.Series<String, Number> series2 = new XYChart.Series<>();
int green = 0;
int yellow = 0;
List<String> compGreen = new ArrayList<String>();
List<String> compyellow = new ArrayList<String>();
for (List<String> ent : dc.getFuncTypeOrg().values()) {
if (ent.size() > 10) {
for (int k = 0; k < ent.size(); k++) {
if (ent.get(k).equals("hasType")) {
if (ent.get(k+1).equals("Green")) {
series.getData().add(
new XYChart.Data<String, Number>(ent.get(k-2), 1));
}
else if(ent.get(k+1).equals("yellow")){
series2.getData().add(
new XYChart.Data<String, Number>(ent.get(k-2), 1));
}
}
}
}
}
barchart.getYAxis().setVisible(false);
barchart.getYAxis().setOpacity(0);
barchart.getData().addAll(series, series2);
barchart.setBarGap(1);
barchart.setCategoryGap(20);
}
}

Resources