Статьи

Пример списка JavaFX

Это пример списка приложений, созданных с использованием JavaFX. Приложение представляет собой список задач (todo). Это приложение имеет функции для добавления, обновления и удаления элементов в списке. Данные списка хранятся в реляционной базе данных HSQLDB. Приложение обращается к базе данных с помощью API JDBC (Java Database Connectivity). Приложение упаковано в виде исполняемого файла JAR.

JavaFX 2.2, Java SE 7 и HSQLDB 2.3.2 используются для сборки приложения.

В этой статье подробно описывается создание приложения. Содержание этого документа:

1. Установите базу данных HSQL

1.1. О HSQLDB

Реляционная база данных HSQL используется для хранения данных задач. В этом разделе — получить и установить базу данных.

HSQLDB (HyperSQL DataBase) — это программное обеспечение реляционной базы данных SQL, написанное на Java и работающее в JVM. Это небольшой, быстрый многопоточный и транзакционный механизм баз данных с таблицами в оперативной памяти и на диске и поддерживает встроенный и серверный режимы. Это включает в себя драйвер JDBC.

1.2. Скачать базу данных

Загрузите программное обеспечение базы данных по ссылке для скачивания на сайте http://hsqldb.org/ . В этом случае HSQLDB версии 2.3.2 загружается. Загруженный файл является файлом ZIP. Распакуйте ZIP-файл в любой каталог по вашему выбору. ZIP-файл распаковывается в папку hsqldb-2.3.2\hsqldb . Это домашний (или установочный) каталог.

На этом установка завершена. Установленная база данных содержит пользовательскую документацию, драйвер JDBC, исполняемые файлы базы данных и служебные программы. Каталог установки содержит каталоги /doc и /lib (в дополнение к другим).

В каталоге /doc есть руководства пользователя.

Каталог /lib содержит следующие файлы JAR, которые обычно используются:

  • hsqldb.jar : У этого есть ядро ​​базы данных, драйвер JDBC и инструмент доступа к базе данных GUI.
  • sqltool.jar : У этого есть инструмент доступа к базе данных командной строки SQL.

2. Создайте базу данных приложения и таблицу

2.1. Создать базу данных Todos

Инструмент доступа к базе данных GUI используется для создания и доступа к базе данных. Из командной строки DOS запустите это:

1
> java -cp "X:\JCG\articles\A JavaFX List Example\hsqldb-2.3.2\hsqldb\lib\hsqldb.jar" org.hsqldb.util.DatabaseManagerSwing

ПРИМЕЧАНИЕ. Файл hsqldb.jar должен находиться в пути к классам.
Откроется диалоговое окно Connect GUI, как показано ниже.

1

Введите в диалог следующую информацию:

  • Недавние настройки: <Не выбирайте ничего сейчас. В следующий раз для подключения к базе данных выберите настройку, созданную сейчас.>
  • Имя параметра: <введите имя> todo db setting
  • Тип: <select> Автономное ядро ​​базы данных HSQL
  • Драйвер: <выберите> hsqldb.jdbcDriver
  • URL: <введите путь к файлу базы данных> jdbc: hsqldb: file: << filepath — см. Примечание ниже для получения дополнительной информации об указании пути >>
  • Пользователь: <оставьте пустым>
  • Пароль: <оставьте пустым>

Примечание по пути к файлу URL: путь к файлу может быть указан как относительный или абсолютный путь. Относительный путь относительно текущего каталога; например, jdbc:hsqldb:file:db\TODOS_DB в URL-адресе создает каталог с именем db и базу данных TODOS_DB в нем. Пример с абсолютным путем — jdbc:hsqldb:file:X:\JCG\articles\A JavaFX List Example\db\TODOS_DB .

Нажмите ОК.

Это создает базу данных с именем TODOS_DB в указанном каталоге. Это также открывает окно диспетчера HSQLDatabase. В этом окне есть области, отображающие структуру базы данных, запись SQL и подробности результатов. Окно показано ниже.

2

2.2 Создание таблицы Todo

Таблица задач состоит из трех столбцов: идентификатор, имя и описание.

  • id: это уникальное целое число, сгенерированное системой базы данных. Это определяется как столбец IDENTITY.

Столбец IDENTITY — это INTEGER, автоматически генерируемый генератором последовательности базы данных. По умолчанию значения столбцов начинаются с 1 и увеличиваются на 1. Когда выполняется вставка в таблицу, значение столбца id автоматически заполняется новым порядковым номером. Синтаксис для вставки и извлечения значения столбца id показан ниже в этой статье (см. Раздел 5. Создание кода доступа к базе данных ).

  • name: это определяется как VARCHAR (50), а не ноль.
  • описание: это определяется как VARCHAR (500).

В окне HSQLDatabase Manager введите следующий SQL-скрипт и выполните его.

1
2
3
4
5
CREATE TABLE TODO_TABLE (
    id INTEGER GENERATED BY DEFAULT AS IDENTITY,
    name VARCHAR(50) NOT NULL,
    description VARCHAR(500)
);

Это создает таблицу задач. Вновь созданную таблицу можно просмотреть в области структуры базы данных.

ПРИМЕЧАНИЕ. Этот шаг необходимо выполнить, прежде чем идти дальше. Код приложения предполагает, что база данных и таблица созданы.

3. Приложение

Задачи отображаются в виде списка, где каждая задача является элементом списка. Когда задача выбирается в списке, имя и описание отображаются в текстовом поле и текстовой области соответственно. Эти данные можно редактировать. Есть кнопки для создания задачи, удаления и сохранения. Сообщение о состоянии показывает последнее выполненное действие.

GUI отображается в окне. Данные, отображаемые в списке и текстовом поле / области, доступны из базы данных todo. База данных открывается при запуске приложения и закрывается при закрытии приложения.

Ниже показан графический интерфейс завершенного приложения.

3

3.1. Классы приложений

Приложение состоит из трех классов Java.

  • Todo.java: этот класс представляет элемент todo.
  • TodoApp.java: Этот класс является основным приложением с графическим интерфейсом и логикой выполнения программы.
  • TodoDataAccess.java: у этого класса есть функции для доступа к базе данных todo.

3.1.1 Todo.java

Todo.java классом Todo.java . У todo есть свойства name и description. Это также поддерживает уникальное поле идентификатора.

3.1.2 TodoDataAccess.java

Этот класс имеет функции для доступа к базе данных todo и данным. Его функции:

  • Подключиться и закрыть базу данных
  • Вставлять, обновлять, удалять, запрашивать и проверять данные в базе данных

3.1.3 TodoApp.java

Этот класс является основной прикладной программой. В нем есть функции для запуска приложения, его закрытия, создания пользовательского интерфейса и подключения графического интерфейса пользователя и приложения к коду доступа к данным.

4. Создайте графический интерфейс

На этом этапе графический интерфейс создается без доступа к базе данных и обработчикам событий действий для кнопок. Только список подключен к слушателю изменения выбора списка.

Приложение отображает некоторые предварительно определенные данные задач в списке. Элементы списка задач могут быть выбраны, и соответствующие имя и описание задачи отображаются в соответствующем текстовом поле / области.

Ниже показан код класса TodoApp.java, за которым следует его описание.

4.1.Код

TodoApp.java:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.paint.Color;
import javafx.scene.control.ListView;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.control.Button;
import javafx.scene.control.Tooltip;
import javafx.scene.text.Text;
import javafx.geometry.Pos;
import javafx.geometry.Insets;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.List;
import java.util.ArrayList;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
 
public class TodoApp extends Application {
    private ListView<Todo> listView;
    private ObservableList<Todo> data;
    private TextField nametxt;
    private TextArea desctxt;
    private Text actionstatus;
 
    public static void main(String [] args) {
        Application.launch(args);
    }
 
    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Todo App - version 1");
 
        // gridPane layout
        GridPane grid = new GridPane();
        grid.setAlignment(Pos.CENTER);
        grid.setHgap(15);
        grid.setVgap(20);
        grid.setPadding(new Insets(25, 25, 25, 25));
         
        // list view, listener and list data
        listView = new ListView<>();
        listView.getSelectionModel().selectedIndexProperty().addListener(
                new ListSelectChangeListener());
        data = getListData();
        listView.setItems(data);
        grid.add(listView, 1, 1); // col = 1, row = 1
         
        // todo name label and text fld - in a hbox
        Label namelbl = new Label("Todo Name:");
        nametxt = new TextField();
        nametxt.setMinHeight(30.0);
        nametxt.setPromptText("Enter todo name (required).");
        nametxt.setPrefColumnCount(20);
        nametxt.setTooltip(new Tooltip(
            "Item name (5 to 50 chars length)"));
        HBox hbox = new HBox();
        hbox.setSpacing(10);
        hbox.getChildren().addAll(namelbl, nametxt);
 
        // todo desc text area in a scrollpane
        desctxt = new TextArea();
        desctxt.setPromptText("Enter description (optional).");
        desctxt.setWrapText(true);
        ScrollPane sp = new ScrollPane();
        sp.setContent(desctxt);
        sp.setFitToWidth(true);
        sp.setFitToHeight(true);
        sp.setPrefHeight(300);
        sp.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        sp.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
         
        // todo hbox (label + text fld), scrollpane - in a vbox
        VBox vbox = new VBox();
        vbox.setSpacing(10);
        vbox.getChildren().addAll(hbox, sp);
 
        grid.add(vbox, 2, 1); // col = 2, row = 1
         
        // new and delete buttons
        Button newbtn = new Button("New");
        Button delbtn = new Button("Delete");
        HBox hbox2 = new HBox(10);
        hbox2.getChildren().addAll(newbtn, delbtn);
        grid.add(hbox2, 1, 2); // col = 1, row = 2
 
        // save button to the right anchor pane and grid
        Button savebtn = new Button("Save");
        AnchorPane anchor = new AnchorPane();
        AnchorPane.setRightAnchor(savebtn, 0.0);
        anchor.getChildren().add(savebtn);     
        grid.add(anchor, 2, 2); // col = 2, row = 2
 
        // action message (status) text
        actionstatus = new Text();
        actionstatus.setFill(Color.FIREBRICK);
        actionstatus.setText("");  
        grid.add(actionstatus, 1, 3); // col = 1, row = 3
 
        // scene
        Scene scene = new Scene(grid, 750, 400); // width=750, height=400
        primaryStage.setScene(scene);
        primaryStage.show();
         
        // initial selection; statement does nothing if no data
        listView.getSelectionModel().selectFirst();
    } // start()
 
    private class ListSelectChangeListener implements ChangeListener<Number> {
        @Override
        public void changed(ObservableValue<? extends Number> ov,
                Number old_val, Number new_val) {
            if ((new_val.intValue() < 0) || (new_val.intValue() >= data.size())) {
                return; // invalid data
            }  
            // set name and desc fields for the selected todo
            Todo todo = data.get(new_val.intValue());
            nametxt.setText(todo.getName());
            desctxt.setText(todo.getDesc());
            actionstatus.setText(todo.getName() + " - selected");  
        }
    }
 
    private ObservableList<Todo> getListData() {
        List<Todo> list = new ArrayList<>(); // initial list data
        list.add(new Todo("Work", "Work on JCG's example article."));
        list.add(new Todo("Grocery", "Get apples, milk and bread."));
        list.add(new Todo("Calls", "Call kid brother."));
        list.add(new Todo("Read book",
            "Magnificent Obcession, by Lloyd C. Douglas."));
        ObservableList<Todo> data = FXCollections.observableList(list);
        return data;
    }
}

4.2. Описание кода

4.2.1 Классы JavaFX

Далее описываются классы JavaFX, используемые для создания графического интерфейса:

  • Класс Stage используется для создания главного окна приложения.
  • GridPane используется для размещения элементов управления (кнопок, текстовых полей и т. Д.) В виде сетки строк и столбцов.
  • HBox и VBox HBox свои дочерние элементы управления в одном горизонтальном или вертикальном ряду соответственно.
  • ListView используется для отображения вертикального прокручиваемого списка элементов todo, из которых пользователь может выбирать.
  • Label и Text используются для метки имени todo и текстовых полей.
  • TextArea используется для поля описания задачи. Поле помещается в ScrollPane , так что текст можно прокручивать.
  • Button управления используется для новых, сохранить и удалить кнопки.
  • Text используется для отображения статуса действия.

4.2.2 Расположение элементов управления

Макет панели сетки имеет 3 строки и 2 столбца. Ячейка, в которой расположены элементы управления, выглядит следующим образом:

  • Строка 1 цв 1 имеет вид списка.
  • Метка Todo и текстовое поле помещаются в hbox.
  • Строка 1 столбец 2 содержит hbox и текстовую область описания задачи — в vbox.
  • Ряд 2 столбец 1 имеет новые кнопки и кнопки удаления — в hbox.
  • Ряд 2 столбец 2 имеет кнопку сохранения.
  • Строка 3 столбец 1 имеет текст статуса.

4.2.3 Слушатель изменения списка

Слушатель изменения выбора списка типа ChangeListener<Number> присоединен к представлению списка:

1
listView.getSelectionModel().selectedIndexProperty().addListener(new changeListener());

Когда элемент списка выбран, его имя и описание отображаются в текстовых полях.

4.2.4 Данные списка

Представление списка заполняется данными из коллекции ObservableList<Todo> — в методе start() :

1
2
3
data = getListData();
 
listView.setItems(data);

В этом разделе данные списка создаются в программе. Метод getListData () создает задачи и возвращает их в виде коллекции ObservableList <Todo>.

4.3 Исходный код

Это версия 1 приложения. Два класса недавно созданы. Классы:

  • Todo.java
  • TodoApp.java

ПРИМЕЧАНИЕ. Чтобы скомпилировать код и запустить приложение, jfxrt.jar (библиотека JavaFX) должен находиться в пути к классам. Для Java 7 это можно найти по адресу: <JRE_HOME>/lib/jfxrt.jar .

5. Создайте код доступа к базе данных

Данные списка задач хранятся и доступны из базы данных. TODO_TABLE в базе данных TODO_DB хранит подробности задач. База данных и таблица уже созданы ранее (см. Раздел 2. Создание базы данных и таблицы приложения ). Обратите внимание, что приложение предполагает, что база данных создана до доступа к ней.

В этом разделе описывается код доступа к базе данных. Класс TodoDataAccess.java имеет код. Этот код использует API JDBC (Java Database Connectivity) для доступа к базе данных TODO_DB.

ПРИМЕЧАНИЕ . Графический интерфейс приложения связан с доступом к базе данных в следующем разделе ( 6. Проводной интерфейс с доступом к базе данных ).

Этот класс имеет методы для:

  • Подключиться к базе данных todo
  • Закройте базу данных todo
  • Прочитать все строки таблицы задач в коллекцию List
  • Вставить строку в таблицу задач
  • Проверьте, существует ли имя задачи в таблице задач
  • Удалить строку из таблицы задач
  • Обновить строку в таблице задач

Ниже показан код и детали класса TodoDataAccess.java .

5.1 Получить соединение

Конструктор имеет код для доступа к базе данных и получает объект Connection . Этот объект подключения используется для чтения или обновления данных базы данных. Свойства соединения устанавливаются как автоматическая фиксация, т.е. транзакция фиксируется при вставке, обновлении или удалении без явной фиксации. Соединение имеет обновляемый тип.

Статический метод DriverManager's getConnection() использует URL для подключения к базе данных.

1
2
3
4
5
6
7
8
public TodoDataAccess()
            throws SQLException, ClassNotFoundException {
        Class.forName("org.hsqldb.jdbc.JDBCDriver" );
        conn = DriverManager.getConnection(
    "jdbc:hsqldb:file:db/TODOS_DB;ifexists=true;shutdown=true", "", "");
        conn.setAutoCommit(true);
        conn.setReadOnly(false);
    }

5.2 Получить все строки

Этот метод извлекает все строки из таблицы todo и возвращает коллекцию List элементов Todo.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public List<Todo> getAllRows()
        throws SQLException {
    String sql = "SELECT * FROM " + todoTable + " ORDER BY name";
    PreparedStatement pstmnt = conn.prepareStatement(sql);
    ResultSet rs = pstmnt.executeQuery();
    List<Todo> list = new ArrayList<>();
    while (rs.next()) {
        int i = rs.getInt("id");
        String s1 = rs.getString("name");
        String s2 = rs.getString("desc");
        list.add(new Todo(i, s1, s2));
    }
    pstmnt.close(); // also closes related result set
    return list;       
}

5.3 Вставить строку

Этот метод вставляет строку в таблицу задач. Метод возвращает идентификатор для новой строки задачи.

Значение id — это порядковый номер, сгенерированный системой баз данных. Это столбец IDENTITY. Ключевое слово DEFAULT (в операторе INSERT) используется для столбца IDENTITY, что приводит к автоматически сгенерированному значению для столбца. Смотрите раздел 2.2. Создать таблицу Todo для получения подробной информации о создании столбца IDENTITY.

Метод PreparedStatement's getGeneratedKeys() извлекает ResultSet со вновь созданным значением столбца идентификаторов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public int insertRow(Todo todo)
        throws SQLException {
    String dml =
        "INSERT INTO " + todoTable + " VALUES (DEFAULT, ?, ?)";
    PreparedStatement pstmnt = conn.prepareStatement(dml,
        PreparedStatement.RETURN_GENERATED_KEYS);
    pstmnt.setString(1, todo.getName());
    pstmnt.setString(2, todo.getDesc());
    pstmnt.executeUpdate(); // returns insert count
    // get identity column value
    ResultSet rs = pstmnt.getGeneratedKeys();
    rs.next();
    int id = rs.getInt(1);
    pstmnt.close();
    return id;
}

5.4 Проверьте, существует ли имя Todo

Этот метод проверяет, существует ли имя задачи в таблице задач. Обратите внимание, что приложение позволяет только уникальные имена задач.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public boolean nameExists(Todo todo)
            throws SQLException {
        String sql = "SELECT COUNT(id) FROM " + todoTable +
            " WHERE name = ? AND id <> ?";
        PreparedStatement pstmnt = conn.prepareStatement(sql);
        pstmnt.setString(1, todo.getName());
        pstmnt.setInt(2, todo.getId());
        ResultSet rs = pstmnt.executeQuery();
        rs.next();
        int count = rs.getInt(1);
        pstmnt.close();
        if (count > 0) {
            return true;
        }
        return false;
    }

5.5 Удалить строку

Этот метод удаляет строку из таблицы задач для заданной задачи, если она существует. Обратите внимание, что исключение не выдается, если ни одна строка не удалена.

1
2
3
4
5
6
7
8
public void deleteRow(Todo todo)
        throws SQLException {
    String dml = "DELETE FROM " + todoTable + " WHERE id = ?";
    PreparedStatement pstmnt = conn.prepareStatement(dml);
    pstmnt.setInt(1, todo.getId());
    pstmnt.executeUpdate(); // returns delete count (0 for none)
    pstmnt.close();
}

5.6 Обновить строку

Этот метод обновляет существующую строку в таблице задач с любыми изменениями свойств задачи.

01
02
03
04
05
06
07
08
09
10
11
public void updateRow(Todo todo)
        throws SQLException {
    String dml = "UPDATE " + todoTable + " SET name = ?, desc = ? " +
        " WHERE id = ?";
    PreparedStatement pstmnt = conn.prepareStatement(dml);
    pstmnt.setString(1, todo.getName());
    pstmnt.setString(2, todo.getDesc());
    pstmnt.setInt(3, todo.getId());
    pstmnt.executeUpdate(); // returns update count
    pstmnt.close();
}

5.7 Закрыть базу данных

Этот метод закрывает соединение с базой данных и закрывает базу данных.

1
2
3
4
public void closeDb()
            throws SQLException {
        conn.close();
  }

5.8 Исходный код

Это версия 2 приложения. Недавно создан один класс — TodoDataAccess.java. Там нет никаких изменений в других. Классы:

  • Todo.java
  • TodoDataAccess.java

ПРИМЕЧАНИЕ . Классы скомпилированы. Нет программы для запуска.

6. Проводной графический интерфейс с доступом к базе данных

Это законченное приложение.

Графический интерфейс приложения подключен к базе данных. Приложение обновлено, чтобы иметь следующие функции:

Добавьте новый элемент todo в список.

  • Нажмите на новую кнопку; введите имя и описание поля.
  • Нажмите кнопку Сохранить. Это вставит недавно введенный todo в базу данных. Новый элемент todo будет добавлен в список.
  • Приложение проверяет, что введенное имя todo имеет длину от 5 до 50 символов и является уникальным в списке.
  • При вводе нового todo, запись можно отменить, нажав кнопку удаления.

Обновите элемент списка задач в списке.

  • Выберите задачу из списка. Отредактируйте поля имени и / или описания.
  • Нажмите кнопку Сохранить. Это сохраняет обновленную задачу в базе данных и обновляет список после проверки.

Удалить элемент todo в списке.

  • Выберите элемент todo из списка.
  • Нажмите кнопку удаления. Это удаляет задачу из базы данных и списка.

Приложение запускается и закрывается.

  • При запуске приложения все задачи в базе данных загружаются в список.
  • При закрытии приложения база данных закрывается.

6.1 Кодирование

В этом разделе приложение обновлено:

  • Новые кнопки, сохранить и удалить связаны с соответствующим обработчиком событий.
  • Код обработчика обращается к базе данных.
  • Методы запуска и закрытия приложения обращаются к базе данных.

Код доступа к базе данных уже построен в предыдущем разделе ( 5. Создать код доступа к базе данных ).

6.1.1 Об обработчике событий

Обработчик события типа ActionEvent используется в качестве обработчика события действия кнопки. Интерфейс EventHandler реализован для этой цели. Свойство обработчика кнопки установлено как button.setOnaction(someHandler) .
Это общее для трех кнопок в этом приложении — новая, удалить и сохранить.

6.2 Создать новый Todo

Когда пользователь нажимает новую кнопку, в представлении списка создается новый элемент задачи, и приложение предлагает пользователю ввести имя и описание нового задачи.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private class NewButtonListener implements EventHandler<ActionEvent> {
        @Override
        public void handle(ActionEvent e) {
            actionstatus.setText("New");
            // creates a todo at first row with name NEW todo and
            // selects it
            Todo todo = new Todo(0, "NEW Todo", ""); // 0 = dummy id
            int ix = 0;
            data.add(ix, todo);
            listView.getSelectionModel().clearAndSelect(ix);
            nametxt.clear();
            desctxt.clear();
            nametxt.setText("NEW Todo");
            nametxt.requestFocus();
        }
    }

6.3 Сохранить Тодо

Когда кнопка сохранения нажата, приложение:

  • Проверяет имя todo на его длину (от 5 до 50 символов)
  • Проверяет, существует ли имя в базе данных
  • Вставляет задачу в базу данных

Обратите внимание, что этот обработчик событий используется для функций вставки и обновления.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
private class SaveButtonListener implements EventHandler<ActionEvent> {
        @Override
        public void handle(ActionEvent ae) {
            int ix = listView.getSelectionModel().getSelectedIndex();
            if (ix < 0) { // no data selected or no data
                return;
            }
            String s1 = nametxt.getText();
            String s2 = desctxt.getText();
            // validate name
            if ((s1.length() < 5) || (s1.length() > 50)) {
                actionstatus.setText(
                    "Name must be 5 to 50 characters in length");
                nametxt.requestFocus();
                nametxt.selectAll();
                return;
            }
            // check if name is unique
            Todo todo = data.get(ix);
            todo.setName(s1);
            todo.setDesc(s2);
            if (isNameAlreadyInDb(todo)) {
                actionstatus.setText("Name must be unique!");
                nametxt.requestFocus();
                return;
            }
            if (todo.getId() == 0) { // insert in db (new todo)
                int id = 0;
                try {
                    id = dbaccess.insertRow(todo);
                }
                catch (Exception e) {
                    displayException(e);
                }              
                todo.setId(id);
                data.set(ix, todo);
                actionstatus.setText("Saved (inserted)");
            }
            else { // db update (existing todo)
                try {
                    dbaccess.updateRow(todo);
                }
                catch (Exception e) {
                    displayException(e);
                }
                actionstatus.setText("Saved (updated)");   
            } // end-if, insert or update in db
            // update list view with todo name, and select it
            data.set(ix, null); // required for refresh
            data.set(ix, todo);
            listView.getSelectionModel().clearAndSelect(ix);
            listView.requestFocus();
        }
    }
 
    private boolean isNameAlreadyInDb(Todo todo) {
        boolean bool = false;
        try {
            bool = dbaccess.nameExists(todo);
        }
        catch (Exception e) {
            displayException(e);
        }      
        return bool;
    }

6.4 Удалить или отменить Todo

Действие кнопки удаления выполняет две функции:

  • Отменяет новое задание, которое вводится, но еще не сохраняется.
  • Удаляет выбранный (существующий) элемент todo из списка и базы данных.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private class DeleteButtonListener implements EventHandler<ActionEvent> {
        @Override
        public void handle(ActionEvent ae) {
            int ix = listView.getSelectionModel().getSelectedIndex();
            if (ix < 0) { // no data or none selected
                return;
            }
            Todo todo = data.remove(ix);   
            try {
                dbaccess.deleteRow(todo);
            }
            catch (Exception e) {
                displayException(e);
            }                  
            actionstatus.setText("Deleted");
            // set next todo item after delete
            if (data.size() == 0) {
                nametxt.clear();
                desctxt.clear();
                return; // no selection
            }      
            ix = ix - 1;       
            if (ix < 0) {
                ix = 0;
            }
            listView.getSelectionModel().clearAndSelect(ix);
            // selected ix data (not set by list listener);
            // requires this is set
            Todo itemSelected = data.get(ix);
            nametxt.setText(itemSelected.getName());
            desctxt.setText(itemSelected.getDesc());
            listView.requestFocus();
        }
    }

6.5 Запуск и закрытие приложения

Методы init() и stop() класса JavaFX- Application используются для инициализации и закрытия приложения. Они переопределены в приложении. Метод init имеет код для доступа к базе данных при запуске приложения. У метода stop есть код для закрытия базы данных при закрытии приложения.

Кроме того, после запуска приложения список задач заполняется данными базы данных (вместо данных, созданных в программе в более ранней версии 1). Это заменяет код из версии 1. Код метода запуска data = getListData() заменяется на data = getDbData() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public void init() {
    try {
        dbaccess = new TodoDataAccess();
    }
    catch (Exception e) {
        displayException(e);
    }
}
@Override
public void stop() {
    try {
        dbaccess.closeDb();
    }
    catch (Exception e) {
        displayException(e);
    }
}
private ObservableList<Todo> getDbData() {
    List<Todo> list = null;
    try {
        list = dbaccess.getAllRows();
    }
    catch (Exception e) {
        displayException(e);
    }
    ObservableList<Todo> dbData = FXCollections.observableList(list);
    return dbData;
}
@Override
public void start(Stage primaryStage) {
    ...
    data = getDbData();
    listView.setItems(data);
    ...

6.6 Исходный код

Это версия 3 приложения, и является окончательной. Один класс, TodoApp.java , изменен. Там нет никаких изменений в других. Классы:

  • Todo.java
  • TodoDataAccess.java
  • TodoApp.java

НОТА:

  • Для компиляции приложения jfxrt.jar должен находиться в пути к классам.
  • Для запуска приложения jfxrt.jar и hsqldb.jar должны находиться в пути к классам.

7. Разверните как файл JAR

7.1 Создание исполняемого файла JAR: todoapp.jar

Утилита javafxpackager с createjar команды createjar используется для создания исполняемого файла JAR для приложения.

  • Создайте каталог с именем: deploy
  • Создайте два подкаталога в каталоге deploy : src и dest
  • Поместите все файлы class приложения в каталог src
  • Перейдите в каталог deploy введите в командной строке DOS следующую команду:
1
> javafxpackager -createjar -appclass TodoApp -srcdir src -outdir dest -outfile todoapp -v -classpath hsqldb.jar

Это создает исполняемый файл JAR приложения. Убедитесь, что файл todoapp.jar создан в каталоге dest .

7.2 Запустите приложение

Скопируйте созданный файл todoapp.jar каталог deploy (или любой). Обратите внимание, что перед запуском приложения требуется следующее:

  • hsqldb.jar file в каталоге (или в пути к классам)
  • База данных приложения и таблица предварительно созданы (see section Создание базы данных приложения и таблицы ).

Запустите приложение одним из следующих способов:

(а) Из командной строки DOS:

1
> java -jar todoapp.jar

(b) Дважды щелкните файл todoapp.jar .

8. Загрузите исходный код Java