Question:
(Painter App Modification) Incorporate the RGBA color chooser you created in the Color Chooser app into the Painter app so that the user can choose any drawing color. Changing a Slider's value should update the color swatch displayed to the user and set the brushColor instance variable to the current Color.
//Ex1303ModifiedPainterApp.java
package ex1303modifiedpainterapp;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Ex1303ModifiedPainterApp extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root =
FXMLLoader.load(getClass().getResource("Painter.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Painter"); // displayed in window's title bar
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
//Painter.fxml
//PainterController.java
package ex1303modifiedpainterapp;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
public class PainterController {
// enum representing pen sizes
private enum PenSize {
SMALL(2),
MEDIUM(4),
LARGE(6);
private final int radius;
PenSize(int radius) {this.radius = radius;}
public int getRadius() {return radius;}
};
// instance variables that refer to GUI components
@FXML private RadioButton blackRadioButton;
@FXML private RadioButton redRadioButton;
@FXML private RadioButton greenRadioButton;
@FXML private RadioButton blueRadioButton;
@FXML private RadioButton smallRadioButton;
@FXML private RadioButton mediumRadioButton;
@FXML private RadioButton largeRadioButton;
@FXML private Pane drawingAreaPane;
@FXML private ToggleGroup colorToggleGroup;
@FXML private ToggleGroup sizeToggleGroup;
// instance variables for managing Painter state
private PenSize radius = PenSize.MEDIUM; // radius of circle
private Paint brushColor = Color.BLACK; // drawing color
// set user data for the RadioButtons
public void initialize() {
// user data on a control can be any Object
blackRadioButton.setUserData(Color.BLACK);
redRadioButton.setUserData(Color.RED);
greenRadioButton.setUserData(Color.GREEN);
blueRadioButton.setUserData(Color.BLUE);
smallRadioButton.setUserData(PenSize.SMALL);
mediumRadioButton.setUserData(PenSize.MEDIUM);
largeRadioButton.setUserData(PenSize.LARGE);
}
// handles drawingArea's onMouseDragged MouseEvent
@FXML
private void drawingAreaMouseDragged(MouseEvent e) {
Circle newCircle = new Circle(e.getX(), e.getY(),
radius.getRadius(), brushColor);
drawingAreaPane.getChildren().add(newCircle);
}
// handles color RadioButton's ActionEvents
@FXML
private void colorRadioButtonSelected(ActionEvent e) {
// user data for each color RadioButton is the corresponding Color
brushColor =
(Color) colorToggleGroup.getSelectedToggle().getUserData();
}
// handles size RadioButton's ActionEvents
@FXML
private void sizeRadioButtonSelected(ActionEvent e) {
// user data for each size RadioButton is the corresponding PenSize
radius =
(PenSize) sizeToggleGroup.getSelectedToggle().getUserData();
}
// handles Undo Button's ActionEvents
@FXML
private void undoButtonPressed(ActionEvent event) {
int count = drawingAreaPane.getChildren().size();
// if there are any shapes remove the last one added
if (count > 0) {
drawingAreaPane.getChildren().remove(count - 1);
}
}
// handles Clear Button's ActionEvents
@FXML
private void clearButtonPressed(ActionEvent event) {
drawingAreaPane.getChildren().clear(); // clear the canvas
}
}
//ColorChooser.java
// Fig. 13.8: ColorChooser.java
// Main application class that loads and displays the ColorChooser's GUI.
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ColorChooser extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root =
FXMLLoader.load(getClass().getResource("ColorChooser.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Color Chooser");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
//ColorChooser.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.shape.Rectangle?>
<GridPane hgap="8.0" style="-fx-background-color: white;" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ColorChooserController">
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" />
<ColumnConstraints hgrow="SOMETIMES" />
<ColumnConstraints hgrow="SOMETIMES" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children><Label text="Red" /><Label text="Green" GridPane.rowIndex="1" /><Label text="Blue" GridPane.rowIndex="2" /><Label text="Alpha" GridPane.rowIndex="3" /><Slider fx:id="redSlider" max="255.0" GridPane.columnIndex="1" /><Slider fx:id="greenSlider" max="255.0" GridPane.columnIndex="1" GridPane.rowIndex="1" /><Slider fx:id="blueSlider" max="255.0" GridPane.columnIndex="1" GridPane.rowIndex="2" /><Slider fx:id="alphaSlider" blockIncrement="0.1" max="1.0" value="1.0" GridPane.columnIndex="1" GridPane.rowIndex="3" /><TextField fx:id="redTextField" prefWidth="50.0" text="0" GridPane.columnIndex="2" /><TextField fx:id="greenTextField" prefWidth="50.0" text="0" GridPane.columnIndex="2" GridPane.rowIndex="1" /><TextField fx:id="blueTextField" prefWidth="50.0" text="0" GridPane.columnIndex="2" GridPane.rowIndex="2" /><TextField fx:id="alphaTextField" prefWidth="50.0" text="1.0" GridPane.columnIndex="2" GridPane.rowIndex="3" />
<Circle fill="DODGERBLUE" radius="40.0" stroke="BLACK" strokeType="INSIDE" GridPane.columnIndex="3" GridPane.rowSpan="2147483647" /><Rectangle fx:id="colorRectangle" arcHeight="5.0" arcWidth="5.0" 100.0" stroke="BLACK" strokeType="INSIDE" 100.0" GridPane.columnIndex="3" GridPane.rowSpan="2147483647" />
</children>
<padding>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
</padding>
</GridPane>
//ColorChooserController
// Fig. 13.9: ColorChooserController.java
// Controller for the ColorChooser app
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class ColorChooserController {
// instance variables for interacting with GUI components
@FXML private Slider redSlider;
@FXML private Slider greenSlider;
@FXML private Slider blueSlider;
@FXML private Slider alphaSlider;
@FXML private TextField redTextField;
@FXML private TextField greenTextField;
@FXML private TextField blueTextField;
@FXML private TextField alphaTextField;
@FXML private Rectangle colorRectangle;
// instance variables for managing
private int red = 0;
private int green = 0;
private int blue = 0;
private double alpha = 1.0;
public void initialize() {
// bind TextField values to corresponding Slider values
redTextField.textProperty().bind(
redSlider.valueProperty().asString("%.0f"));
greenTextField.textProperty().bind(
greenSlider.valueProperty().asString("%.0f"));
blueTextField.textProperty().bind(
blueSlider.valueProperty().asString("%.0f"));
alphaTextField.textProperty().bind(
alphaSlider.valueProperty().asString("%.2f"));
// listeners that set Rectangle's fill based on Slider changes
redSlider.valueProperty().addListener(
new ChangeListener() {
@Override
public void changed(ObservableValue ov,
Number oldValue, Number newValue) {
red = newValue.intValue();
colorRectangle.setFill(Color.rgb(red, green, blue, alpha));
}
}
);
greenSlider.valueProperty().addListener(
new ChangeListener() {
@Override
public void changed(ObservableValue ov,
Number oldValue, Number newValue) {
green = newValue.intValue();
colorRectangle.setFill(Color.rgb(red, green, blue, alpha));
}
}
);
blueSlider.valueProperty().addListener(
new ChangeListener() {
@Override
public void changed(ObservableValue ov,
Number oldValue, Number newValue) {
blue = newValue.intValue();
colorRectangle.setFill(Color.rgb(red, green, blue, alpha));
}
}
);
alphaSlider.valueProperty().addListener(
new ChangeListener() {
@Override
public void changed(ObservableValue ov,
Number oldValue, Number newValue) {
alpha = newValue.doubleValue();
colorRectangle.setFill(Color.rgb(red, green, blue, alpha));
}
}
);
}
}
Answer:
Painter.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<BorderPane prefHeight="480.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="PainterController">
<left>
<VBox maxHeight="1.7976931348623157E308" spacing="8.0" BorderPane.alignment="CENTER">
<children>
<TitledPane text="Drawing Color"> <!-- Title of the Title pane -->
<content>
<VBox spacing="8.0">
<children> <!-- Add the radio button to the Drawing color -->
<RadioButton fx:id="blackRadioButton" mnemonicParsing="false" onAction="#colorRadioButtonSelected" selected="true" text="Black">
<toggleGroup>
<ToggleGroup fx:id="colorToggleGroup" />
</toggleGroup>
</RadioButton>
<RadioButton fx:id="redRadioButton" mnemonicParsing="false" onAction="#colorRadioButtonSelected" text="Red" toggleGroup="$colorToggleGroup" />
<RadioButton fx:id="greenRadioButton" mnemonicParsing="false" onAction="#colorRadioButtonSelected" text="Green" toggleGroup="$colorToggleGroup" />
<RadioButton fx:id="blueRadioButton" mnemonicParsing="false" onAction="#colorRadioButtonSelected" text="Blue" toggleGroup="$colorToggleGroup" />
</children>
</VBox>
</content>
</TitledPane>
<TitledPane text="Pen Size"> <!-- Title of the Pen size -->
<content>
<VBox spacing="8.0">
<children> <!-- Add the radio button to the pen size -->
<RadioButton fx:id="smallRadioButton" mnemonicParsing="false" onAction="#sizeRadioButtonSelected" text="Small">
<toggleGroup>
<ToggleGroup fx:id="sizeToggleGroup" />
</toggleGroup>
</RadioButton>
<RadioButton fx:id="mediumRadioButton" mnemonicParsing="false" onAction="#sizeRadioButtonSelected" selected="true" text="Medium" toggleGroup="$sizeToggleGroup" />
<RadioButton fx:id="largeRadioButton" mnemonicParsing="false" onAction="#sizeRadioButtonSelected" text="Large" toggleGroup="$sizeToggleGroup" />
</children>
</VBox>
</content>
</TitledPane>
<!-- undo and clear button-->
<Button fx:id="undoButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#undoButtonPressed" text="Undo" />
<Button fx:id="clearButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#clearButtonPressed" text="Clear" />
</children>
<BorderPane.margin>
<Insets right="8.0" />
</BorderPane.margin>
</VBox>
</left>
<center>
<Pane fx:id="drawingAreaPane" onMouseDragged="#drawingAreaMouseDragged" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: white;" BorderPane.alignment="CENTER" />
</center>
<padding>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
</padding>
</BorderPane>
output: