Assignment: Build Your Own JavaFX TODO App
Objective: Create a functional and visually appealing TODO application in JavaFX that allows users to manage their tasks effectively.
What You Will Build
You will create a JavaFX application that provides the following features:
- Add Tasks: Users can add new tasks to a list.
- Mark Tasks as Completed: Users can toggle the completion status of a task.
- Delete Tasks: Users can remove tasks from the list.
- Task Details: Optional: Users can click on a task to view or edit additional details about it (e.g., due date, priority).
- Persistent Storage: Optional: Save tasks to a file so that they persist when the application is closed and reopened.
Features Your App Should Have
- User Interface:
- A text input field for entering new tasks.
- Buttons to add, mark as completed, undo, or delete tasks.
- A list that displays all tasks dynamically.
- Task Representation:
- Each task should display:
- The task name.
- Completion status (e.g., strikethrough text or a checkbox).
- Include actions to “Complete/Undo” and “Delete” tasks.
- Each task should display:
- Styling:
- Use JavaFX styles to differentiate completed tasks visually (e.g., grayed-out text with strikethrough).
Suggested Classes and Properties
To manage your TODO list effectively, you will need the following:
1. Supporting Class for Tasks
Define a Task
class to represent a single TODO item.
Suggested Properties:
StringProperty name
: Stores the task’s name.BooleanProperty isCompleted
: Tracks whether the task is completed.- (Optional)
StringProperty details
: Additional details about the task (e.g., due date or priority).
Suggested Methods:
- Constructor: Initializes the task with a name and default values for other properties.
public Task(String name) { ... }
- Getters and Setters: Provide access to the properties.
public String getName(); public void setName(String name); public boolean isCompleted(); public void setCompleted(boolean isCompleted);
- (Optional) toString: Return a user-friendly representation of the task.
public String toString() { ... }
2. Managing the Task List
Use an ObservableList<Task>
to store tasks and dynamically update the UI when changes occur.
Suggested Methods:
- addTask(String name):
- Adds a new task to the list.
- Parameter:
String name
- The name of the task to add.
- deleteTask(Task task):
- Removes a task from the list.
- Parameter:
Task task
- The task to delete.
- toggleTaskCompletion(Task task):
- Toggles the completion status of a task.
- Parameter:
Task task
- The task to update.
Hints for UI Design
- Input Section:
- Use a
TextField
for entering new tasks and aButton
to add them to the list.
- Use a
- Task Display:
- Use a
ListView<Task>
for displaying tasks. - Implement a custom cell factory to render tasks with the “Complete/Undo” and “Delete” buttons.
- Use a
- Dynamic Updates:
- Use property bindings to dynamically update the UI when task properties change.
- Styling:
- Use CSS-like styles for completed tasks:
-fx-text-fill: gray; -fx-strikethrough: true;
- Use CSS-like styles for completed tasks:
Challenge Features
- Task Details View:
- Create a modal dialog that allows users to view or edit task details, such as due dates or priority levels.
- Task Filtering:
- Add options to filter tasks (e.g., show only incomplete tasks or tasks with a specific priority).
- Persistence:
- Save the task list to a file (e.g., JSON or plain text) and reload it when the application starts.
Encouragement
- Think Creatively:
- Experiment with UI layouts (e.g., add tabs or multiple views for organizing tasks).
- Explore JavaFX Properties:
- Learn how
StringProperty
andBooleanProperty
work and how they simplify UI updates.
- Learn how
- Adapt the Suggestions:
- Use the suggested methods and properties as a guide, but don’t feel obligated to follow them exactly.
What You’ll Learn
- How to use JavaFX properties and bindings to create dynamic UIs.
- How to work with
ObservableList
to manage and display collections. - How to implement custom cell factories for
ListView
. - Optional: How to persist data for application reusability.
Submission Requirements
- The full JavaFX application code.
- Screenshots of the app in action, showing:
- Adding, completing, and deleting tasks.
- (Optional) Task details view or filtering.
- A short description of how your app works and the decisions you made during development.
Here is the completed code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ToDoApp extends Application {
// Observable list to store the TODO items
private final ObservableList<ToDoItem> toDoList = FXCollections.observableArrayList();
public static class ToDoItem {
private String name;
private boolean isCompleted;
public ToDoItem(String name, boolean isCompleted) {
this.name = name;
this.isCompleted = isCompleted;
}
public String getName() {
return name;
}
public boolean isCompleted() {
return isCompleted;
}
public void setName(String name) {
this.name = name;
}
public void setCompleted(boolean completed) {
isCompleted = completed;
}
}
@Override
public void start(Stage stage) {
VBox root = new VBox(10);
root.setPadding(new Insets(20));
// Input field for new TODO items
TextField inputField = new TextField();
inputField.setPromptText("Enter a task");
// Add button
Button addButton = new Button("Add");
addButton.setOnAction(e -> {
String task = inputField.getText().trim();
if (!task.isEmpty()) {
toDoList.add(new ToDoItem(task, false));
inputField.clear();
}
});
// List view to display TODO items
ListView<ToDoItem> listView = new ListView<>(toDoList);
listView.setCellFactory(lv -> new ToDoCell());
// Add the input field, button, and list to the layout
HBox inputBox = new HBox(10, inputField, addButton);
root.getChildren().addAll(new Label("Things To Do"), inputBox, listView);
// Scene setup
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.setTitle("TODO App");
stage.show();
}
private static class ToDoCell extends ListCell<ToDoItem> {
@Override
protected void updateItem(ToDoItem item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
HBox cellBox = new HBox(10);
Label nameLabel = new Label(item.getName());
nameLabel.setStyle(item.isCompleted() ? "-fx-text-fill: gray; -fx-strikethrough: true;" : "-fx-text-fill: black;");
// Complete/Undo button
Button completeButton = new Button(item.isCompleted() ? "Undo" : "Complete");
completeButton.setOnAction(e -> {
item.setCompleted(!item.isCompleted());
nameLabel.setStyle(item.isCompleted() ? "-fx-text-fill: gray; -fx-strikethrough: true;" : "-fx-text-fill: black;");
completeButton.setText(item.isCompleted() ? "Undo" : "Complete");
});
// Delete button
Button deleteButton = new Button("Delete");
deleteButton.setOnAction(e -> getListView().getItems().remove(item));
cellBox.getChildren().addAll(nameLabel, completeButton, deleteButton);
setGraphic(cellBox);
}
}
}
public static void main(String[] args) {
launch(args);
}
}