JavaFX教程:第二部分:Model 和 TableView

Oct 8, 2014 • Updated: Jan 15, 2015

Screenshot AddressApp Part 2

第二部分的主題

  • 創建一個 模型 類。
  • ObservableList 使用模型類。
  • 使用 ControllersTableView 上顯示數據。

創建 模型 類。

我們需要一個模型類來保存聯繫人信息到我們的通訊錄中。在模型包中 (ch.makery.address.model) 添加一個叫 Person的類。Person 類將會有一些變量,名字,地址和生日。將以下代碼添加到類。在代碼後,我將解釋一些 JavaFX 的細節。

Person.java

package ch.makery.address.model;

import java.time.LocalDate;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 * Model class for a Person.
 *
 * @author Marco Jakob
 */
public class Person {

	private final StringProperty firstName;
	private final StringProperty lastName;
	private final StringProperty street;
	private final IntegerProperty postalCode;
	private final StringProperty city;
	private final ObjectProperty<LocalDate> birthday;

	/**
	 * Default constructor.
	 */
	public Person() {
		this(null, null);
	}

	/**
	 * Constructor with some initial data.
	 * 
	 * @param firstName
	 * @param lastName
	 */
	public Person(String firstName, String lastName) {
		this.firstName = new SimpleStringProperty(firstName);
		this.lastName = new SimpleStringProperty(lastName);

		// Some initial dummy data, just for convenient testing.
		this.street = new SimpleStringProperty("some street");
		this.postalCode = new SimpleIntegerProperty(1234);
		this.city = new SimpleStringProperty("some city");
		this.birthday = new SimpleObjectProperty<LocalDate>(LocalDate.of(1999, 2, 21));
	}

	public String getFirstName() {
		return firstName.get();
	}

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

	public StringProperty firstNameProperty() {
		return firstName;
	}

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

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

	public StringProperty lastNameProperty() {
		return lastName;
	}

	public String getStreet() {
		return street.get();
	}

	public void setStreet(String street) {
		this.street.set(street);
	}

	public StringProperty streetProperty() {
		return street;
	}

	public int getPostalCode() {
		return postalCode.get();
	}

	public void setPostalCode(int postalCode) {
		this.postalCode.set(postalCode);
	}

	public IntegerProperty postalCodeProperty() {
		return postalCode;
	}

	public String getCity() {
		return city.get();
	}

	public void setCity(String city) {
		this.city.set(city);
	}

	public StringProperty cityProperty() {
		return city;
	}

	public LocalDate getBirthday() {
		return birthday.get();
	}

	public void setBirthday(LocalDate birthday) {
		this.birthday.set(birthday);
	}

	public ObjectProperty<LocalDate> birthdayProperty() {
		return birthday;
	}
}

解釋

  • 在JavaFX中,對一個模型類的所有屬性使用 Properties是很常見的. 一個 Property 允許我們, 打個比方, 當 lastName 或其他屬性被改變時自動收到通知, 這有助於我們保持視圖與數據的同步,閱讀 Using JavaFX Properties and Binding 學習更多關於 Properties 的內容。
  • birthday, 我們使用了 LocalDate 類型, 這在 Date and Time API for JDK 8 中是一個新的部分.

人員列表

我們的應用主要管理的數據是一羣人的信息.讓我們在 MainApp 類裏面創建一個 Person 對象的列表。稍後其他所有的控制器類將存取 MainApp 的核心列表。

ObservableList

我們處理JavaFX的view classes需要在人員列表發生任何改變時都被通知. 這是很重要的,不然視圖就會和數據不同步.爲了達到這個目的,JavaFX引入了一些新的集合類.

在這些集合中, 我們需要的是ObservableList. 將以下代碼增加到MainApp類的開頭去創建一個新的ObservableList. 我們也會增加一個構造器去創建一些樣本數據和一個公共的getter方法:

MainApp.java

// ... AFTER THE OTHER VARIABLES ...

	/**
	 * The data as an observable list of Persons.
	 */
	private ObservableList<Person> personData = FXCollections.observableArrayList();

	/**
	 * Constructor
	 */
	public MainApp() {
		// Add some sample data
		personData.add(new Person("Hans", "Muster"));
		personData.add(new Person("Ruth", "Mueller"));
		personData.add(new Person("Heinz", "Kurz"));
		personData.add(new Person("Cornelia", "Meier"));
		personData.add(new Person("Werner", "Meyer"));
		personData.add(new Person("Lydia", "Kunz"));
		personData.add(new Person("Anna", "Best"));
		personData.add(new Person("Stefan", "Meier"));
		personData.add(new Person("Martin", "Mueller"));
	}
  
	/**
	 * Returns the data as an observable list of Persons. 
	 * @return
	 */
	public ObservableList<Person> getPersonData() {
		return personData;
	}
  
    // ... THE REST OF THE CLASS ...

The PersonOverviewController

現在我們終於要將數據加入到表格中了,我們需要一個控制器爲了PersonOverview.fxml,.

  1. view包下創建一個名爲 PersonOverviewController.java的普通java類(我們需要將這個類放在和PersonOverview.fxml相同的包下, 不然SceneBuilder會找不到它 - 至少在當前的版本).
  2. 我們需要增加一些實例變量來訪問表格和在視圖中的標籤.這些屬性和一些方法有一個特殊的 @FXML 註解. 這對於fxml文件訪問私有屬性和私有方法來說是必需的. 當將一切都在fxml文件中設置好之後, 應用程序會在fxml文件被載入時自動地填充這些變量. 讓我們添加以下的代碼:
Note: 記住要使用 javafx imports, 而不是awt和swing!

PersonOverviewController.java

package ch.makery.address.view;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import ch.makery.address.MainApp;
import ch.makery.address.model.Person;

public class PersonOverviewController {
    @FXML
    private TableView<Person> personTable;
    @FXML
    private TableColumn<Person, String> firstNameColumn;
    @FXML
    private TableColumn<Person, String> lastNameColumn;

    @FXML
    private Label firstNameLabel;
    @FXML
    private Label lastNameLabel;
    @FXML
    private Label streetLabel;
    @FXML
    private Label postalCodeLabel;
    @FXML
    private Label cityLabel;
    @FXML
    private Label birthdayLabel;

    // Reference to the main application.
    private MainApp mainApp;

    /**
     * The constructor.
     * The constructor is called before the initialize() method.
     */
    public PersonOverviewController() {
    }

    /**
     * Initializes the controller class. This method is automatically called
     * after the fxml file has been loaded.
     */
    @FXML
    private void initialize() {
    	// Initialize the person table with the two columns.
        firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
        lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
    }

    /**
     * Is called by the main application to give a reference back to itself.
     * 
     * @param mainApp
     */
    public void setMainApp(MainApp mainApp) {
        this.mainApp = mainApp;

        // Add observable list data to the table
        personTable.setItems(mainApp.getPersonData());
    }
}

可能需要解釋一下這段代碼:

  • 所有fxml文件需要訪問的屬性和方法必須加上 @FXML 註解.實際上,只有在私有的情況下才需要, 但是讓它們保持私有並且用註解標記的方式更好!
  • initialize() 方法在fxml文件完成載入時被自動調用. 那時, 所有的FXML屬性都應已被初始化.
  • 我們在表格列上使用setCellValueFactory(...) 來確定爲特定列使用Person對象的某個屬性. 箭頭 -> 表示我們在使用Java 8的 Lambdas 特性. (另一個選擇是使用 PropertyValueFactory, 但它不是類型安全的).

連接 MainApp 和 PersonOverviewController

setMainApp(...) 必須被 MainApp 類調用. 這讓我們可以訪問MainApp對象並得到Persons的列表和其他東西. 用以下代碼替換showPersonOverview() 方法. 它包含了新增的兩行:

MainApp.java - new showPersonOverview() method

/**
 * Shows the person overview inside the root layout.
 */
public void showPersonOverview() {
    try {
        // Load person overview.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
        AnchorPane personOverview = (AnchorPane) loader.load();

        // Set person overview into the center of root layout.
        rootLayout.setCenter(personOverview);

        // Give the controller access to the main app.
        PersonOverviewController controller = loader.getController();
        controller.setMainApp(this);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

將View與Controller掛鉤

我們快要完成了! 但是有件小事被遺漏了: 至今沒有告訴 PersonOverview.fxml使用的是哪個控制器以及元素與控制器中的屬性的對應關係.

  1. 使用SceneBuilder 打開 PersonOverview.fxml.

  2. 打開左邊的 Controller 組選擇PersonOverviewController 作爲 controller class.
    Set Controller Class(設置控制器類)

  3. Hierarchy 組選擇TableView 並選擇 Code 組將 personTable 作爲 fx:id.
    Set TableView fx:id

  4. 對列做相同的事並且將 firstNameColumn and lastNameColumn分別作爲 fx:id .

  5. 對在第二列的 each label , 選擇對應的 fx:id.
    Set Label fx:id

  6. 重要事項: 回到eclipse並且 refresh the entire AddressApp project (F5). 這是必要的因爲有時候eclipse並不知道在Scene Builder中作出的改變.


啓動應用程序

當你現在啓動了你的應用,你應該看到了類似這篇博客開頭的截圖的程序界面.

恭喜!

接下去做什麼?

Tutorial Part 3 我們將加入增加,刪除和編輯人員的功能.

一些你可能感興趣的其他文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章