Java Beans   «Prev 

Packaging FancyButton Bean - Exercise

Objective: Use the JAR utility to package the FancyButton Bean.
In this exercise, include the v option so that the JAR utility will output the results of the operation.
  1. The Java class files,
  2. manifest file, and
  3. image resources
for the FancyButton Bean are included with the course example source code, which you can download by clicking the Resources button on the menu bar to the left.

// FancyButtonFX.java

import javafx.beans.property.*;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Control;
import javafx.scene.control.SkinBase;
import javafx.scene.input.*;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;

public class FancyButtonFX extends Region {

    private final StringProperty label = new SimpleStringProperty("Push Me");
    private final BooleanProperty sticky = new SimpleBooleanProperty(false);
    private final BooleanProperty down = new SimpleBooleanProperty(false);
    private final BooleanProperty hasFocus = new SimpleBooleanProperty(false);

    private final Canvas canvas = new Canvas();

    public FancyButtonFX() {
        getChildren().add(canvas);
        setFocusTraversable(true);
        setFont(Font.font("System", 12));

        setOnMousePressed(e -> {
            down.set(!down.get());
            draw();
        });

        setOnMouseReleased(e -> {
            if (!sticky.get()) {
                fireEvent(new ActionEvent());
                down.set(false);
                draw();
            }
        });

        setOnKeyPressed(e -> {
            if (e.getCode() == KeyCode.ENTER || e.getCode() == KeyCode.SPACE) {
                if (sticky.get()) {
                    down.set(!down.get());
                } else {
                    down.set(true);
                    fireEvent(new ActionEvent());
                    down.set(false);
                }
                draw();
            }
        });

        focusedProperty().addListener((obs, oldVal, newVal) -> {
            hasFocus.set(newVal);
            draw();
        });

        widthProperty().addListener(e -> draw());
        heightProperty().addListener(e -> draw());
        label.addListener(e -> resizeToFit());

        resizeToFit();
    }


    public final String getLabel() {
        return label.get();
    }

    public final void setLabel(String value) {
        label.set(value);
    }

    public final StringProperty labelProperty() {
        return label;
    }

    public final boolean isSticky() {
        return sticky.get();
    }

    public final void setSticky(boolean value) {
        sticky.set(value);
    }

    public final BooleanProperty stickyProperty() {
        return sticky;
    }

    public final boolean isDown() {
        return down.get();
    }

    public final void setFont(Font font) {
        canvas.getGraphicsContext2D().setFont(font);
        resizeToFit();
    }

    private void resizeToFit() {
        Font font = Font.font("System", 12);
        Text text = new Text(label.get());
        text.setFont(font);
        double width = text.getLayoutBounds().getWidth() + 30;
        double height = text.getLayoutBounds().getHeight() + 20;
        setPrefSize(width, height);
        canvas.setWidth(width);
        canvas.setHeight(height);
        draw();
    }

    private void draw() {
        GraphicsContext g = canvas.getGraphicsContext2D();
        double w = getWidth();
        double h = getHeight();

        // Background with 3D border
        g.setFill(Color.LIGHTGRAY);
        g.fillRect(0, 0, w, h);

        boolean isPressed = down.get();
        g.setStroke(Color.BLACK);
        g.setLineWidth(1);
        g.strokeRect(0, 0, w - 1, h - 1);
        g.setStroke(isPressed ? Color.DARKGRAY : Color.WHITE);
        g.strokeLine(1, h - 2, 1, 1);
        g.strokeLine(1, 1, w - 2, 1);

        // Draw text
        g.setFill(Color.BLACK);
        g.setFont(Font.font("System", 12));
        double textWidth = g.getFontMetrics().computeStringWidth(label.get());
        double textHeight = g.getFontMetrics().getLineHeight();
        double offset = isPressed ? 2 : 0;
        g.fillText(label.get(), (w - textWidth) / 2 + offset, (h + textHeight) / 2 - 4 + offset);

        // Focus rectangle
        if (hasFocus.get()) {
            g.setStroke(Color.GRAY);
            g.strokeRect(4, 4, w - 8, h - 8);
        }
    }


 

    public void addActionHandler(EventHandler handler) {
        addEventHandler(ActionEvent.ACTION, handler);
    }

    public void removeActionHandler(EventHandler handler) {
        removeEventHandler(ActionEvent.ACTION, handler);
    }
}


  • Canvas is used for custom painting, mimicking paint(Graphics g) behavior from AWT.
  • Property bindings (StringProperty, BooleanProperty) replace direct variable access for JavaFX conventions.
  • The focus and key handling is preserved.
  • ActionEvent dispatching is implemented with JavaFX's event mechanism.
  • Styling can later be externalized via CSS for modern UIs.



Executing the JAR utility

The order of the options used when executing the JAR utility is important.
For example, the usage of the options fcm indicates that the name of the JAR file is specified as the first argument, while the name of the external manifest file is specified as the second argument.
If you were to reverse the options and make them mcf, for example, you would need to reverse the order of these files.

When the JAR utility is finished packaging the FancyButton Bean, copy and paste the output in the text box, below.
Click the Submit when finished.