我正在创建简单的JavaFX应用程序。我希望我的模型层完全独立于JavaFX - 没有StringProperty
,IntegerProperty
等作为字段。我希望它是POJO。这样做的主要原因是我希望它是Serializable。
我创建了DataRepository
- 简单的类CRUD接口及其一些实现,因此我可以随时更改存储数据的位置 - XML文件,SQLite数据库或其他任何内容。我还必须以某种方式将我的数据存储与JavaFX连接(以在TableView中显示其内容),因此我决定创建包含我的存储库的ObservableList
实现。我的问题是 - 还有其他方法吗? ObservableList
包含大约30种要实现的方法,看起来我做错了。
我的(简化)模型:
public class Movie implements Serializable {
private String title;
private String director;
public Movie() {
}
public Movie(String title, String director) {
this.title = title;
this.director = director;
}
// Getters and setters, equals etc...
}
MovieRepository:
public interface MovieRepository {
public void add(Movie movie);
public void remove(String title);
public void remove(int index);
public Movie get(String title);
public Movie get(int index);
public List<Movie> getAll();
}
主视图的控制器:
public class MainController {
@FXML
private TableView<Movie> movieTable;
@FXML
private TableColumn<Movie, String> movieTitleColumn;
@FXML
private Label titleLabel;
private MovieRepository movies = new DBMovieRepository(); //MovieRepository implementation which uses SQLite DB to store data
private MainApp app;
@FXML
private void initialize() {
movieTable.setItems(new ObservableMovies(movies));
// ObservableMovies is my implementation of ObservableList
// It basically wraps methods from MovieRepository
// and notifies listeners
showMovieDetails(null);
movieTitleColumn.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getTitle()));
movieTable.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> showMovieDetails(newValue));
}
private void showMovieDetails(Movie movie) {
if(movie != null) {
titleLabel.setText(movie.getTitle());
} else {
titleLabel.setText("");
}
}
@FXML
private void handleNew() {
Movie movie = new Movie();
app.showNewMovieDialog(movie);
movieTable.getItems().add(movie);
}
public void setApp(MainApp app) {
this.app = app;
}
}
答案 0 :(得分:1)
这里有几个选项(可能更多),本网站上的其他问题也包含这些选项。但是,为了方便起见,我也在这里总结一下。
<强> 1。使用JavaFX属性并使类Serializable
您可以使用自定义序列化表单执行此操作。创建JavaFX属性transient
并实现readObject
和writeObject
来存储它们包装的值:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Objects;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Movie implements Serializable {
private transient StringProperty title = new SimpleStringProperty();
private transient StringProperty director = new SimpleStringProperty();
public Movie() {
}
public Movie(String title, String director) {
setTitle(title);
setDirector(director);
}
@Override
public int hashCode() {
return Objects.hash(getDirector(), getTitle());
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Movie other = (Movie) obj;
return Objects.equals(getTitle(), other.getTitle())
&& Objects.equals(getDirector(), other.getDirector());
}
public final StringProperty titleProperty() {
return this.title;
}
public final String getTitle() {
return this.titleProperty().get();
}
public final void setTitle(final String title) {
this.titleProperty().set(title);
}
public final StringProperty directorProperty() {
return this.director;
}
public final String getDirector() {
return this.directorProperty().get();
}
public final void setDirector(final String director) {
this.directorProperty().set(director);
}
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
s.defaultReadObject();
title = new SimpleStringProperty((String) s.readObject());
director = new SimpleStringProperty((String) s.readObject());
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeObject(getTitle());
s.writeObject(getDirector());
}
}
<强> 2。使用具有“绑定属性”的POJO。
有关详细信息,请参阅JavaBean wrapping with JavaFX Properties。简而言之:
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Movie {
private String title ;
private String director ;
private final PropertyChangeSupport propertySupport ;
public Movie(String title, String director) {
this.title = title ;
this.director = director ;
this.propertySupport = new PropertyChangeSupport(this);
}
public Movie() {
this("", "");
}
public String getTitle() {
return title ;
}
public String setTitle(String title) {
String oldTitle = this.title ;
this.title = title ;
propertySupport.firePropertyChange("title", oldTitle, title);
}
// similarly for director...
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener(listener);
}
// hashCode and equals...
}
如果想将您的存储库包装为可观察列表,而是使用使用可观察列表的存储库实现包装它:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class ObservableMovieRepository implements MovieRepository {
private final MovieRepository repository ;
private final ObservableList<Movie> movieList;
public ObservableMovieRepository(MovieRepository repository) {
this.repository = repository ;
this.movieList = FXCollections.observableArrayList(repository.getAll());
}
@Override
public void add(Movie movie) {
repository.add(movie);
movieList.add(movie);
}
@Override
public void remove(String title) {
Movie movie = get(title);
repository.remove(title);
movieList.remove(title);
}
@Override
public void remove(int index) {
repository.remove(index);
movieList.remove(index);
}
@Override
public Movie get(String title) {
return repository.get(title);
}
@Override
public Movie get(int index) {
return movieList.get(index);
}
@Override
public ObservableList<Movie> getAll() {
return movieList ;
}
}
这使用标准ObservableList
实现,该实现在创建时复制现有列表,并且实现使该列表与包装的存储库中的列表保持同步。现在您的UI代码可以执行
ObservableMovieRepository movies = new ObservableMovieRepository(new DBMovieRepository());
// ...
movieTable.setItems(movies.getAll());
使用上面的Movie
课程,您只需
movieTitleColumn.setCellValueFactory(cellData -> cellData.getValue().titleProperty());
如果您使用POJO版本,则可以
movieTitleColumn.setCellValueFactory(cellData -> {
try {
return new JavaBeanStringPropertyBuilder()
.bean(cellData.getValue())
.name("title")
.build();
} catch (Exception e) { throw new RuntimeException(e); }
}
答案 1 :(得分:0)
这里似乎有多个问题,所以我不确定,如果我理解正确的话,但我会尝试将它分开一点。
我希望我的模型层完全独立于JavaFX - 没有 StringProperty,IntegerProperty等作为字段。我想要它 POJO。
您可以将自己的属性标记为transient
。然后你只需要将它们包装在你的值周围,它将兼容JavaFX和Serializable
。您只需将更改传播回支持属性即可。
我还必须以某种方式将我的数据存储与JavaFX连接(以显示 它在TableView中的内容),所以我决定创建我的实现 包含我的存储库的ObservableList。我的问题是 - 是吗? 任何其他方式?
关于此的信息非常有限,我真的不知道,为什么你需要创建自己的ObservableList
实现,但为了保持POJO,你可以保持简单java.util.Collections
in您的bean并提供瞬态ObservableList
,您可以在创建时通过将java.util.List
包装在POJO中来创建。您可以在FXCollections
实用程序类中找到这些方法。
ObservableList包含大约30种要实现的方法,它看起来很像 就像我做错了一样。
如果确实需要实现它,您可以继承ObservableListBase
。