1

I'm trying to get a Method using Fibonacci to bind to a button, which will use a slider as the input value and then print the Fibonacci number in a textfield. I am running into trouble passing the value from my Fibonacci method to the textfield. Any help would be appreciated. This is what I've written so far:

public class Main extends Application {

    private static Slider fibSlider = new Slider(0, 10, 0);
    private static Label indexLabel = new Label();
    private static Label fibNumLabel = new Label();
    private static int colIndex = 0;
    private static int rowIndex = 0;
    private static int topIndex = 0;
    private static int rightIndex = 0;
    private static int leftIndex = 0;
    private static int bottomIndex = 0;
    private static TextField tfIndex;
    private static TextField tfFibNum;
    private static Button butCalcFib = new Button();

    @Override
    public void start(Stage primaryStage) {

        fibSlider.setMajorTickUnit(1);
        fibSlider.setMinorTickCount(0);
        fibSlider.setShowTickLabels(true);
        fibSlider.setShowTickMarks(true);
        fibSlider.setSnapToTicks(true);

        fibSlider.valueProperty().addListener(sl -> {
            tfIndex.setText(Double.toString(fibSlider.getValue()));
        });

        indexLabel.setText("Index: ");
        fibNumLabel.setText("Fibonacci #: ");
        butCalcFib.setText("Calculate Fibonacci");
        //tfFibNum.setText(long.toString(ComputeFibonacci()));

        fibSlider.valueProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
                if (newValue == null) {
                    tfIndex.setText("");
                    return;
                }
                tfIndex.setText(Math.round(newValue.intValue()) + "");
            }
        });

        GridPane mainGPane = buildGPane();
        Scene mainScene = new Scene(mainGPane, 500, 200);

        primaryStage.setTitle(" - Fibonacci Calculator");
        primaryStage.setScene(mainScene);
        primaryStage.show();

    }

    private GridPane buildGPane() {
        GridPane gPane = new GridPane();
        gPane.setAlignment(Pos.CENTER);
        gPane.setPadding(new Insets(topIndex = 10, rightIndex = 10,
            bottomIndex = 10, leftIndex = 10));
        gPane.setHgap(2);
        gPane.setVgap(2);
        gPane.add(fibSlider, colIndex = 1, rowIndex = 3);
        gPane.add(indexLabel, colIndex = 1, rowIndex = 5);
        gPane.add(tfIndex, colIndex = 2, rowIndex = 5);
        gPane.add(butCalcFib, colIndex = 1, rowIndex = 6);
        gPane.add(fibNumLabel, colIndex = 1, rowIndex = 7);
        gPane.add(tfFibNum, colIndex = 2, rowIndex = 7);

        return gPane;
    }

    public Main() {
        tfIndex = new TextField();
        tfFibNum = new TextField();
    }

    public static void ComputeFibonacci() {
        double index = fibSlider.getValue();
        // Find and display the Fibonacci number
        fib((long) index);
    }

    /**
     * The method for finding the Fibonacci number
     */
    public static long fib(long index) {
        if (index == 0) // Base case
        {
            return 0;
        } else if (index == 1) // Base case 
        {
            return 1;
        } else // Reduction and recursive calls 
        {
            return fib(index - 1) + fib(index - 2);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Jake
  • 49
  • 1
  • 1
  • 8

1 Answers1

3

You can simplify your code considerably by letting the slider's listener update both the index and the result fields. Then you only need one slider listener and no button.

    fibSlider.valueProperty().addListener(sl -> {
        long value = (long) fibSlider.getValue();
        tfIndex.setText(Long.toString(value));
        tfFibNum.setText(Long.toString(fib(value)));
    });

image

See also this Q&A about the observer pattern. In the variation below,

  • GridPane allows the slider to span two columns.

  • Invoking the slider's setBlockIncrement() method lets you change the slider using the arrow keys.

  • In the slider's InvalidationListener, you can reference the slider's DoubleProperty value, rather than referencing the enclosing class's fibSlider field.

As tested:

import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

/** @see https://stackoverflow.com/a/37068554/230513 */
public class Main extends Application {

    private final Slider fibSlider = new Slider(0, 10, 0);
    private final Label indexLabel = new Label("Index: ");
    private final Label fibNumLabel = new Label("Fibonacci #: ");
    private final TextField tfIndex = new TextField("0");
    private final TextField tfFibNum = new TextField("0");

    @Override
    public void start(Stage primaryStage) {
        fibSlider.setMajorTickUnit(1);
        fibSlider.setMinorTickCount(0);
        fibSlider.setShowTickLabels(true);
        fibSlider.setShowTickMarks(true);
        fibSlider.setSnapToTicks(true);
        fibSlider.setBlockIncrement(1.0);
        fibSlider.valueProperty().addListener(sl -> {
            long value = ((DoubleProperty) sl).longValue();
            tfIndex.setText(Long.toString(value));
            tfFibNum.setText(Long.toString(fib(value)));
        });

        GridPane mainGPane = buildGPane();
        Scene mainScene = new Scene(mainGPane);
        primaryStage.setTitle("Fibonacci Calculator");
        primaryStage.setScene(mainScene);
        primaryStage.show();
    }

    private GridPane buildGPane() {
        GridPane gPane = new GridPane();
        gPane.setAlignment(Pos.CENTER);
        gPane.setPadding(new Insets(10, 10, 10, 10));
        gPane.setHgap(10);
        gPane.setVgap(10);
        gPane.add(fibSlider, 0, 0, 2, 1);
        gPane.add(indexLabel, 0, 1);
        gPane.add(tfIndex, 1, 1);
        gPane.add(fibNumLabel, 0, 2);
        gPane.add(tfFibNum, 1, 2);
        return gPane;
    }

    /**
     * The method for finding the Fibonacci number
     */
    public long fib(long index) {
        if (index == 0) {
            return 0;
        } else if (index == 1) {
            return 1;
        } else {
            return fib(index - 1) + fib(index - 2);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for the thorough response. That was exactly what I was running into trouble doing. As for simplifying my code, I know I can update both fields like you showed, however I wanted a button that would update the Fibonacci number once it was clicked; thats why I was writing it with a button. – Jake May 06 '16 at 19:19
  • @Jake: You can add your own `EventHandler` with `butCalcFib.setOnAction(…)`, for [example](http://docs.oracle.com/javase/8/javafx/user-interface-tutorial/button.htm#CJHEEACB); update your question if you still have trouble. – trashgod May 06 '16 at 22:33
  • How can I set the action of the button to call the fib method and display it in the tfFibNum textfield? – Jake May 09 '16 at 03:28
  • I wouldn't, as it would have no visible effect; instead, add a listener to `tfIndex` in order to update `tfFibNum` as the user types; see `textProperty().addListener()`, for [example](http://stackoverflow.com/a/31909942/230513). – trashgod May 09 '16 at 08:53