JavaFX中MVC例子理解

       JavaFX可以让你使用GUI组件创建桌面应用程序。一个GUI应用程序执行三个任务:接受用户的输入,处理输入,并显示输出。而一个GUI应用程序包含两个
类型的代码:

  • 领域代码。处理特定领域的数据和遵循业务规范。
  • 交互代码。处理用户输入信息。

       MVC模型最大的优点就是同一组数据可以根据需求在不同的界面或表格中显示。例如你可以在web端,桌面端、工控机等不同UI界面上同时查看同一组数据。。MVC模型对应的有三个模型组件:model,view和controller。如下图所示:

JavaFX中MVC例子理解_第1张图片

        MVC模型介绍。

  • model:由记录数据的领域对象组成。
  • view:显示给用户的界面。
  • controller:处理用户输入,及对用户输入的响应。

       这里有个简单的例子。模型模块PersonTableUtil保存领域数据Person,因为只是显示数据,所有此示例中没有controller模块,显示模块SimplestableView

领域数据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 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 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 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 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 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 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);
}

模型模块PersonTableUtil,将Person数据添加到ObservabList中,作为数据模型返回给View。同时,PersonTableUtil有多个static方法返回TableColumn,进行处理,并根据显示模块的要求进行处理。

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 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.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 getIdColumn() {
        /**
         * 创建显示的列实例。参数Person:列绑定的数据模型。参数Integer:数据模型中数据的类型,类型必须是引用类型。
         *  “Id”是列表头显示的内容。
         */
        TableColumn 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 getFirstNameColumn() {
        TableColumn 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 getLastNameColumn() {
        TableColumn 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 getBirthDateColumn() {
        TableColumn birthDateColumn = new TableColumn<>("Birth Date");
        birthDateColumn.setCellValueFactory(new PropertyValueFactory<>("birthDate"));
        
        return birthDateColumn;
    }
}

SimplestTableView是显示模块,将模型模块中的数据显示出来。

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 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 table = new TableView<>(PersonTableUtil.getPersonList());
        
        // Placeholder。当table没有内容显示时,显示Label内容。
        table.setPlaceholder(new Label("No visible columns and/or data exist."));
        
        // Setup nest table header.
        TableColumn 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 tableView = new TableView<>(PersonTableUtil.getPersonList());
        tableView.setPlaceholder(new Label("No rows to display"));
        
        // 调用PersonTableUtil.getIdColumn方法,返回TableColumn。
        TableColumn idColumn = PersonTableUtil.getIdColumn();
        
        /**
         * 创建TableColumn实例,参数Person表示列中显示数据来自于那里,参数String表示显示数据的类型,参数
         *  First Name是该列显示的列表头内容。
         */
        TableColumn 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 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();
    }
}

启用方法start01的运行结果如下图,02/03方法主要对数据插入不同列,在模型外添加模型数据测试。

JavaFX中MVC例子理解_第2张图片

start02方法和start03方法自测。

      

你可能感兴趣的:(Java,JavaFX,个人学习,mvc,java)