MVC example understanding in JavaFX

       JavaFX lets you create desktop applications using GUI components. A GUI application performs three tasks: accepts user input, processes the input, and displays output. Whereas a GUI application contains two
types of code:

  • field code. Handle domain-specific data and follow business norms.
  • Interactive code. Handle user input.

       The biggest advantage of the MVC model is that the same set of data can be displayed in different interfaces or tables according to requirements. For example, you can view the same set of data at the same time on different UI interfaces such as web, desktop, and industrial computer. . The MVC model corresponds to three model components: model, view and controller. As shown below:

        Introduction to the MVC model.

  • model: Consists of domain objects that record data.
  • view: The interface displayed to the user.
  • controller: handles user input and responds to user input.

       Here is a simple example. The model module PersonTableUtil saves the domain data Person, because it only displays the data, so there is no controller module in this example, and the display module SimplestableView

Domain data Person:

package cn.learnjavafx.ch11;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 * @copyright 2023-2022
 * @package   learnjavafx8.ch11
 * @file      Person.java
 * @date      2023-07-01 21:31
 * @author    qiao wei
 * @version   1.0
 * @brief     模型类。保存数据,字段使用属性,可以通过注册监听器监听数据更新情况,自动更新属性。
 * @history
 */
public class Person {

	/**
	 * @class   Person
	 * @date    2023-07-01 21:31
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   Default constructor.
	 * @param
	 * @return
	 * @throws
	 */
	public Person() {
		this("None", "None", null);
	}

	/**
	 * @class   Person
	 * @date    2023-07-01 21:32
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   Constructor.
	 * @param   firstName 名。
	 * @param   lastName  姓。
	 * @param   birthDate 出生日期。
	 * @return
	 * @throws
	 */
	public Person(String firstName, String lastName, LocalDate birthDate) {
		this.firstName.set(firstName);
		this.lastName.set(lastName);
		this.birthDate.set(birthDate);
	}
	
	public final int getPersonId() {
		return personId.get();
	}
	
	public final ReadOnlyIntegerProperty getPersonIdProperty() {
		return personId.getReadOnlyProperty();
	}

	/**
	 * @class   Person
	 * @date    2023-07-01 21:37
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   Get first name property.
	 * @param
	 * @return  Person first name.
	 * @throws
	 */
	public final String firstName() {
		return firstName.get();
	}

	public final void setFirstName(String firstName) {
		firstNameProperty().set(firstName);
	}

	public final StringProperty firstNameProperty() {
		return firstName;
	}

	public final String lastName() {
		return lastName.get();
	}

	public final void setLastName(String lastName) {
		lastNameProperty().set(lastName);
	}

	public final StringProperty lastNameProperty() {
		return lastName;
	}

	/** birthDate Property */
	public final LocalDate birthDate() {
		return birthDate.get();
	}
	
	public final void setBirthDate(LocalDate birthDate) {
		birthDateProperty().set(birthDate);
	}
	
	public final ObjectProperty<LocalDate> birthDateProperty() {
		return birthDate;
	}

	/**
	 * @class   Person
	 * @date    2023-07-02 10:30
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   Domain specific business rules.
	 * @param   localDate 当地时区日期
	 * @return
	 * @throws
	 */
	public boolean isValidBirthDate(LocalDate localDate) {
		return isValidBirthDate(localDate, new ArrayList<>());
	}
	
	/**
	 * @class   Person
	 * @date    2023-07-08 19:21
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   验证输入的出生日期是否有效。出生日期无效时,将错误日志记录到errorList中。
	 * @param   date 出生日期。
	 * @param   errorList 错误日志。   
	 * @return  出生日期有效返回true,反之返回false。
	 * @throws
	 */
	public boolean isValidBirthDate(LocalDate date, List<String> errorList) {
		if (null == date) {
			return true;
		}

		// Birthdate cannot be in the future
		if (date.isAfter(LocalDate.now())) {
			errorList.add(LocalDate.now().toString() + " : Birth date must not be in future.");

			return false;
		}

		return true;
	}

	/**
	 * @class   Person
	 * @date    2023-07-02 11:51
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   重写方法,验证个人信息是否正确。Domain specific business rules。
	 * @param   errorList 错误信息列表。
	 * @return
	 * @throws
	 */
	public boolean isValidPerson(List<String> errorList) {
		return isValidPerson(this, errorList);
	}

	/**
	 * @class   Person
	 * @date    2023-07-02 11:53
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   重写方法,验证个人信息是否正确,对个人的姓、名、生日进行有效性验证。Domain specific business
	 *           rules。
	 * @param   person 需要验证的个人信息。
	 * @param   errorList 错误信息列表。记录错误信息。 
	 * @return  true 个人信息有效;false 个人信息无效。
	 * @throws
	 */
	public boolean isValidPerson(Person person, List<String> errorList) {
		boolean isValidPerson = true;
		String firstName = person.firstName();

		// 将以下3个判断条件都走一遍,将所有异常信息统计到errorList中
		if (firstName == null || firstName.trim().length() == 0) {
			errorList.add("First name must contain minimum one character.");
			isValidPerson = false;
		}

		String lastName = person.lastName();
		if (null == lastName || 0 == lastName.trim().length()) {
			errorList.add("Last name must contain minimum one character.");
			isValidPerson = false;
		}

		if ( !isValidBirthDate(this.birthDate.get(), errorList)) {
			isValidPerson = false;
		}

		return isValidPerson;
	}
	
	/**
	 * @class   Person
	 * @date    2023-07-02 12:15
	 * @author  qiao wei
	 * @version 1.0
	 * @brief   根据年龄,返回不同的年龄层。
	 * @param
	 * @return  年龄层,枚举类型。根据不同年龄返回不同年龄层。
	 * @throws
	 */
	public AgeCategory getAgeCategory() {
		if (null == birthDate.get()) {
			return AgeCategory.UNKNOWN;
		}
		
		// 计算年龄。
		long years = ChronoUnit.YEARS.between(birthDate.get(), LocalDate.now());
		if (0 <= years && 2 > years) {
			return AgeCategory.BABY;
		} else if (2 <= years && 13 > years) {
			return AgeCategory.CHILD;
		} else if (13 <= years && 19 >= years) {
			return AgeCategory.TEEN;
		} else if (19 < years && 50 >= years) {
			return AgeCategory.ADULT;
		} else if (50 < years) {
			return AgeCategory.SENIOR;
		} else {
			return AgeCategory.UNKNOWN;
		}
	}

	public boolean save(List<String> errorList) {
		boolean isSaved = false;

		if (isValidPerson(errorList)) {
			System.out.println("Saved " + this.toString());
			isSaved = true;
		}

		return isSaved;
	}

	@Override
	public String toString() {
		StringBuilder stringBuilder = new StringBuilder("[personId=");
		stringBuilder.append(personId.get()).
			append(", firstName = ").append(firstName.get()).
			append(", lastName = ").append(lastName.get()).
			append(", birthDate = ").append(birthDate.get()).append("]");

		return stringBuilder.toString();
	}

	/**
	 * @date   2023-07-01 21:33
	 * @author qiao wei
	 * @brief  Person id
	 */
	private final ReadOnlyIntegerWrapper personId =
		new ReadOnlyIntegerWrapper(this, "personId", personSequence.incrementAndGet());
	
	private final StringProperty firstName =
		new SimpleStringProperty(this, "firstName", null);

	private final StringProperty lastName =
		new SimpleStringProperty(this, "lastName", null);

	/**
	 * @date   2023-07-01 21:33
	 * @author qiao wei
	 * @brief  出生日期。
	 */
	private final ObjectProperty<LocalDate> birthDate =
		new SimpleObjectProperty<>(this, "birthDate", null);

	/**
	 * @date   2023-07-01 21:34
	 * @author qiao wei
	 * @brief  Class field. Keeps track of last generated person id.
	 */
	private static AtomicInteger personSequence = new AtomicInteger(0);
}

The model module PersonTableUtil adds Person data to ObservabList and returns it to View as a data model. At the same time, PersonTableUtil has multiple static methods to return TableColumn for processing and processing according to the requirements of the display module.

package cn.learnjavafx.ch13.tableview01;

import java.time.LocalDate;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.PropertyValueFactory;

import cn.learnjavafx.ch11.Person;

/**
 * @copyright 2023-2022
 * @package   cn.learnjavafx.ch13.tableview01
 * @file      PersonTableUtil.java
 * @date      2023-07-05 20:30
 * @author    qiao wei
 * @version   1.0
 * @brief     模型类。方法getPersonList返回与视图绑定的列表。方法getIdColumn,getFirstNameColumn,
 *             getLastNameColumn以列的数据格式返回列表中各项的对应值。
 * @history
 */
public class PersonTableUtil {
    
    /**
     * @class   PersonTableUtil
     * @date    2023-07-06 16:41
     * @author  qiao wei
     * @version 1.0
     * @brief   Default constructor.
     * @param   
     * @return  
     * @throws
     */
    public PersonTableUtil() {}
    
    /**
     * @class   PersonTableUtil
     * @date    2023-07-05 20:32
     * @author  qiao wei
     * @version 1.0
     * @brief   Retrieve an observable list of person.
     * @param   
     * @return  Person列表。要显示的模型。
     * @throws
     */
    public static ObservableList<Person> getPersonList() {
        Person p1 = new Person("Ashwin"
            , "Sharan"
            , LocalDate.of(2012, 10, 11));
        
        Person p2 = new Person("Advik"
            , "Tim"
            , LocalDate.of(2012, 10, 11));
        
        Person p3 = new Person("Layne"
            , "Estes"
            , LocalDate.of(2011, 12, 16));
        
        Person p4 = new Person("Mason"
            , "Boyd"
            , LocalDate.of(2003, 4, 20));
        
        Person p5 = new Person("Babalu"
            , "Sha"
            , LocalDate.of(1980, 1, 10));
        
        return FXCollections.<Person>observableArrayList(p1, p2, p3, p4, p5);
    }
    
    /**
     * @class   PersonTableUtil
     * @date    2023-07-05 20:40
     * @author  qiao wei
     * @version 1.0
     * @brief   Retrieve person Id TableColumn.
     * @param   
     * @return  Id column.
     * @throws
     */
    public static TableColumn<Person, Integer> getIdColumn() {
        /**
         * 创建显示的列实例。参数Person:列绑定的数据模型。参数Integer:数据模型中数据的类型,类型必须是引用类型。
         *  “Id”是列表头显示的内容。
         */
        TableColumn<Person, Integer> personIdCol = new TableColumn<>("Id");
        
        // 列实例绑定模型的对应属性。
        personIdCol.setCellValueFactory(new PropertyValueFactory<>("personId"));
        
        return personIdCol;
    }
    
    /**
     * @class   PersonTableUtil
     * @date    2023-07-05 20:51
     * @author  qiao wei
     * @version 1.0
     * @brief   Retrieve first name TableColumn.
     * @param   
     * @return  First name column.
     * @throws
     */
    public static TableColumn<Person, String> getFirstNameColumn() {
        TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
        firstNameColumn.setCellValueFactory(new PropertyValueFactory<>("firstName"));
        
        return firstNameColumn;
    }
    
    /**
     * @class   PersonTableUtil
     * @date    2023-07-05 20:59
     * @author  qiao wei
     * @version 1.0
     * @brief   Retrieve last name TableColumn.
     * @param   
     * @return  Last name column.
     * @throws
     */
    public static TableColumn<Person, String> getLastNameColumn() {
        TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
        lastNameColumn.setCellValueFactory(new PropertyValueFactory<>("lastName"));
        
        return lastNameColumn;
    }
    
    /**
     * @class   PersonTableUtil
     * @date    2023-07-05 21:00
     * @author  qiao wei
     * @version 1.0
     * @brief   Retrieve birthdate TableColumn.
     * @param   
     * @return  Birthdate column.
     * @throws
     */
    public static TableColumn<Person, LocalDate> getBirthDateColumn() {
        TableColumn<Person, LocalDate> birthDateColumn = new TableColumn<>("Birth Date");
        birthDateColumn.setCellValueFactory(new PropertyValueFactory<>("birthDate"));
        
        return birthDateColumn;
    }
}

SimplestTableView is a display module that displays the data in the model module.

package cn.learnjavafx.ch13.tableview01;

import java.time.LocalDate;

import javafx.application.Application;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;

import cn.learnjavafx.ch11.Person;

/**
 * @copyright 2023-2022
 * @package   cn.learnjavafx.ch13.tableview01
 * @file      SimplestTableView.java
 * @date      2023-07-05 22:52
 * @author    qiao wei
 * @version   1.0
 * @brief     
 * @history
 */
public class SimplestTableView extends Application {
    
    @Override
    public void start(Stage primaryStage) {
        try {
            start03(primaryStage);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
    
    /**
     * @class   SimplestTableView
     * @date    2023-07-05 22:52
     * @author  qiao wei
     * @version 1.0
     * @brief   
     * @param   primaryStage Main window.
     * @return  
     * @throws
     */
    private void start01(Stage primaryStage) throws Exception {
        // Create a TableView and bind model.
        TableView<Person> table = new TableView<>(PersonTableUtil.getPersonList());
        
        /**
         * Add columns to the TableView in order.
         */
        table.getColumns().addAll(PersonTableUtil.getIdColumn()
            , PersonTableUtil.getFirstNameColumn()
            , PersonTableUtil.getLastNameColumn());
        
        // Add a table column in index position.
        table.getColumns().add(2, PersonTableUtil.getBirthDateColumn());
        
        VBox root = new VBox(table);
        root.setStyle("-fx-padding: 10;"
            + "-fx-border-style: solid inside;"
            + "-fx-border-width: 2;"
            + "-fx-border-insets: 5;"
            + "-fx-border-radius: 5;"
            + "-fx-border-color: pink;");        
        Scene scene = new Scene(root);
        
        primaryStage.setScene(scene);
        primaryStage.setTitle("Simplest TableView");
        primaryStage.show();
    }
    
    /**
     * @class   SimplestTableView
     * @date    2023-07-05 22:53
     * @author  qiao wei
     * @version 1.0
     * @brief   设置复合表头,占位符测试。设置表头Name中包含FirstName和LastName。当表格没有内容时,显示占位符内容。
     * @param   primaryStage 主窗体。
     * @return  
     * @throws
     */
    private void start02(Stage primaryStage) throws Exception {
        // Create a TableView with a list of persons.
        TableView<Person> table = new TableView<>(PersonTableUtil.getPersonList());
        
        // Placeholder。当table没有内容显示时,显示Label内容。
        table.setPlaceholder(new Label("No visible columns and/or data exist."));
        
        // Setup nest table header.
        TableColumn<Person, String> nameColumn = new TableColumn<>("Name");
        nameColumn.getColumns().addAll(PersonTableUtil.getFirstNameColumn()
            , PersonTableUtil.getLastNameColumn());
        
        // Inserts columns to the TableView.
        table.getColumns().addAll(PersonTableUtil.getIdColumn(), nameColumn);
        
        /**
         * 在指定列添加列表信息,列从0开始计数。FirstName和LastName设置在复合表头,只算一列。所以插入“出生日期”列只
         *  能在0~2列。
         */
        table.getColumns().add(2, PersonTableUtil.getBirthDateColumn());
        
        VBox root = new VBox(table);
        root.setStyle("-fx-padding: 10;"
            + "-fx-border-style: solid inside;"
            + "-fx-border-width: 2;"
            + "-fx-border-insets: 5;"
            + "-fx-border-radius: 5;"
            + "-fx-border-color: gray;");
        Scene scene = new Scene(root);
        
        primaryStage.setScene(scene);
        primaryStage.setTitle("Simplest TableView02");
        primaryStage.show();
    }
    
    private void start03(Stage primaryStage) {
        // Create a TableView instance and set Placeholder.
        TableView<Person> tableView = new TableView<>(PersonTableUtil.getPersonList());
        tableView.setPlaceholder(new Label("No rows to display"));
        
        // 调用PersonTableUtil.getIdColumn方法,返回TableColumn。
        TableColumn<Person, Integer> idColumn = PersonTableUtil.getIdColumn();
        
        /**
         * 创建TableColumn实例,参数Person表示列中显示数据来自于那里,参数String表示显示数据的类型,参数
         *  First Name是该列显示的列表头内容。
         */
        TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
        
        /**
         * PropertyValueFactory的参数是Person对象的字段,绑定Person的字段显示。
         * In the example shown earlier, a second PropertyValueFactory is set on the second
         *  TableColumn instance. The property name passed to the second PropertyValueFactory is
         *  lastName, which will match the getter method getLastName() of the Person class.
         */
        firstNameColumn.setCellValueFactory(new PropertyValueFactory<>("firstName"));
        
        TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
        lastNameColumn.setCellValueFactory(new PropertyValueFactory<>("lastName"));
        
        tableView.getColumns().addAll(lastNameColumn, firstNameColumn);
        tableView.getColumns().add(0, idColumn);
        
        tableView.getItems().add(new Person("John"
            , "Doe"
            , LocalDate.of(2000, 8, 12)));
        
        VBox root = new VBox(tableView);
        Scene scene = new Scene(root);
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

The running result of the start01 method is shown in the figure below. The 02/03 method mainly inserts different columns of data and adds model data tests outside the model.

The start02 method and the start03 method self-test.

      

Guess you like

Origin blog.csdn.net/weiweiqiao/article/details/131615215