1.列表显示ListView
下面是一个JavaFX ListView的示例代码和使用方法:
public class ListViewExample extends Application {
@Override
public void start(Stage primaryStage) {
// 创建一个可观察的列表,用于存储ListView中的数据
ObservableList<String> items = FXCollections.observableArrayList(
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5"
);
// 创建ListView并将数据项设置为可观察列表
ListView<String> listView = new ListView<>(items);
// 设置ListView的布局
VBox vbox = new VBox(listView);
vbox.setPadding(new Insets(10));
// 创建场景并将布局设置为场景根节点
Scene scene = new Scene(vbox, 200, 200);
// 设置舞台的标题,并将场景设置为舞台的根节点
primaryStage.setTitle("ListView Example");
primaryStage.setScene(scene);
// 显示舞台
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
这是一个简单的JavaFX应用程序,创建了一个包含多个项目的ListView。将项目添加到可观察列表中,然后将其设置为ListView的数据项。最后,将ListView加入一个VBox布局中,并通过一个Scene显示在舞台上。
1.1 JavaFX 中的 ListView 过滤
下面演示如何在 JavaFX 应用程序中过滤 ListView。两个列表由应用程序管理。一个列表包含数据模型中的所有项目。第二个列表包含当前正在查看的项目。作为过滤器存储的比较逻辑片段在两者之间进行调解。
大量使用绑定来保持数据结构与用户选择的内容同步。
此屏幕截图显示了应用程序,其中包含顶行的切换按钮(用于设置过滤器)和包含对象的列表视图。
数据结构
该程序以域模型 Player 和 Player 对象数组开始。
static class Player {
private final String team;
private final String playerName;
public Player(String team, String playerName) {
this.team = team;
this.playerName = playerName;
}
public String getTeam() {
return team;
}
public String getPlayerName() {
return playerName;
}
@Override
public String toString() { return playerName + " (" + team + ")"; }
}
Player 类包含一对字段:team 和playerName。提供了 toString(),以便将对象添加到 ListView(稍后介绍)时,不需要自定义 ListCell 类。
本例的测试数据是美国棒球运动员的列表。
Player[] players = {new Player("BOS", "David Ortiz"),
new Player("BOS", "Jackie Bradley Jr."),
new Player("BOS", "Xander Bogarts"),
new Player("BOS", "Mookie Betts"),
new Player("HOU", "Jose Altuve"),
new Player("HOU", "Will Harris"),
new Player("WSH", "Max Scherzer"),
new Player("WSH", "Bryce Harper"),
new Player("WSH", "Daniel Murphy"),
new Player("WSH", "Wilson Ramos") };
模型
正如文章开头提到的,ListView 过滤是围绕两个列表的管理展开的。所有对象都存储在包装的 ObservableList PlayersProperty 中,当前可查看的对象存储在包装的 FilteredList viewablePlayersProperty 中。 viewablePlayersProperty 是基于playersProperty 构建的,因此对满足 FilteredList 标准的玩家所做的更新也会对 viewablePlayers 进行。
ReadOnlyObjectProperty<ObservableList<Player>> playersProperty =
new SimpleObjectProperty<>(FXCollections.observableArrayList());
ReadOnlyObjectProperty<FilteredList<Player>> viewablePlayersProperty =
new SimpleObjectProperty<FilteredList<Player>>(
new FilteredList<>(playersProperty.get()
));
filterProperty() 可以方便地允许调用者绑定到底层谓词。
ObjectProperty<Predicate<? super Player>> filterProperty =
viewablePlayersProperty.get().predicateProperty();
UI 根是一个 VBox,其中包含一个 ToggleButtons 的 HBox 和一个 ListView。
VBox vbox = new VBox();
vbox.setPadding( new Insets(10));
vbox.setSpacing(4);
HBox hbox = new HBox();
hbox.setSpacing( 2 );
ToggleGroup filterTG = new ToggleGroup();
过滤动作
ToggleButtons 附加了一个处理程序,它将修改 filterProperty。每个 ToggleButton 都在 userData 字段中提供一个谓词。设置过滤器属性时,toggleHandler 使用此提供的谓词。此代码设置特殊情况“显示全部”ToggleButton。
@SuppressWarnings("unchecked")
EventHandler<ActionEvent> toggleHandler = (event) -> {
ToggleButton tb = (ToggleButton)event.getSource();
Predicate<Player> filter = (Predicate<Player>)tb.getUserData();
filterProperty.set( filter );
};
ToggleButton tbShowAll = new ToggleButton("Show All");
tbShowAll.setSelected(true);
tbShowAll.setToggleGroup( filterTG );
tbShowAll.setOnAction(toggleHandler);
tbShowAll.setUserData( (Predicate<Player>) (Player p) -> true);
过滤特定团队的切换按钮是在运行时根据 Players 数组创建的。该流执行以下操作。
-
将球员列表提炼为不同的团队字符串列表
-
为每个团队创建一个 ToggleButton String
-
为每个 ToggleButton 设置一个谓词以用作过滤器
-
收集 ToggleButtons 以添加到 HBox 容器中
List<ToggleButton> tbs = Arrays.asList( players)
.stream()
.map( (p) -> p.getTeam() )
.distinct()
.map( (team) -> {
ToggleButton tb = new ToggleButton( team );
tb.setToggleGroup( filterTG );
tb.setOnAction( toggleHandler );
tb.setUserData( (Predicate<Player>) (Player p) -> team.equals(p.getTeam()) );
return tb;
})
.collect(Collectors.toList());
hbox.getChildren().add( tbShowAll );
hbox.getChildren().addAll( tbs );
列表显示
下一步创建 ListView 并将 ListView 绑定到 viewablePlayersProperty。这使得 ListView 能够根据不断变化的过滤器接收更新。
ListView<Player> lv = new ListView<>();
lv.itemsProperty().bind( viewablePlayersProperty );
程序的其余部分创建一个场景并显示舞台。 onShown 将数据集加载到playersProperty 和viewablePlayersProperty 列表中。尽管两个列表在该特定版本的程序中是同步的,但如果库存过滤器与“无过滤器”完全不同,则无需修改此代码。
vbox.getChildren().addAll( hbox, lv );
Scene scene = new Scene(vbox);
primaryStage.setScene( scene );
primaryStage.setOnShown((evt) -> {
playersProperty.get().addAll( players );
});
primaryStage.show();
完整代码
public class FilterListApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
//
// 测试数据
//
Player[] players = {new Player("BOS", "David Ortiz"),
new Player("BOS", "Jackie Bradley Jr."),
new Player("BOS", "Xander Bogarts"),
new Player("BOS", "Mookie Betts"),
new Player("HOU", "Jose Altuve"),
new Player("HOU", "Will Harris"),
new Player("WSH", "Max Scherzer"),
new Player("WSH", "Bryce Harper"),
new Player("WSH", "Daniel Murphy"),
new Player("WSH", "Wilson Ramos") };
//
// 建立两个玩家列表和一个过滤标准的模型
//
ReadOnlyObjectProperty<ObservableList<Player>> playersProperty =
new SimpleObjectProperty<>(FXCollections.observableArrayList());
ReadOnlyObjectProperty<FilteredList<Player>> viewablePlayersProperty =
new SimpleObjectProperty<FilteredList<Player>>(
new FilteredList<>(playersProperty.get()
));
ObjectProperty<Predicate<? super Player>> filterProperty =
viewablePlayersProperty.get().predicateProperty();
//
// 构建UI界面
//
VBox vbox = new VBox();
vbox.setPadding( new Insets(10));
vbox.setSpacing(4);
HBox hbox = new HBox();
hbox.setSpacing( 2 );
ToggleGroup filterTG = new ToggleGroup();
//
// toggleHandler操作将根据所选择的TB设置过滤器
//
@SuppressWarnings("unchecked")
EventHandler<ActionEvent> toggleHandler = (event) -> {
ToggleButton tb = (ToggleButton)event.getSource();
Predicate<Player> filter = (Predicate<Player>)tb.getUserData();
filterProperty.set( filter );
};
ToggleButton tbShowAll = new ToggleButton("Show All");
tbShowAll.setSelected(true);
tbShowAll.setToggleGroup( filterTG );
tbShowAll.setOnAction(toggleHandler);
tbShowAll.setUserData( (Predicate<Player>) (Player p) -> true);
//
// 从Player对象中创建一个不同的球队列表,然后创建ToggleButtons
//
//
List<ToggleButton> tbs = Arrays.asList( players)
.stream()
.map( (p) -> p.getTeam() )
.distinct()
.map( (team) -> {
ToggleButton tb = new ToggleButton( team );
tb.setToggleGroup( filterTG );
tb.setOnAction( toggleHandler );
tb.setUserData( (Predicate<Player>) (Player p) -> team.equals(p.getTeam()) );
return tb;
})
.collect(Collectors.toList());
hbox.getChildren().add( tbShowAll );
hbox.getChildren().addAll( tbs );
//
// 创建一个绑定到viewablePlayers属性的ListView
//
ListView<Player> lv = new ListView<>();
lv.itemsProperty().bind( viewablePlayersProperty );
vbox.getChildren().addAll( hbox, lv );
Scene scene = new Scene(vbox);
primaryStage.setScene( scene );
primaryStage.setOnShown((evt) -> {
playersProperty.get().addAll( players );
});
primaryStage.show();
}
public static void main(String args[]) {
launch(args);
}
static class Player {
private final String team;
private final String playerName;
public Player(String team, String playerName) {
this.team = team;
this.playerName = playerName;
}
public String getTeam() {
return team;
}
public String getPlayerName() {
return playerName;
}
@Override
public String toString() { return playerName + " (" + team + ")"; }
}
}
1.2 LIstView添加和删除
首先,你需要设置一个ListView和一个ObservableList。
public class Main extends Application{
ObservableList<String> data = FXCollections.observableArrayList();
ListView<String> listView = new ListView<String>(data);
...
}
接下来,你可以想这样添加列表项:
public void addItem(String item) {
data.add(item);
}
然后,你可以使用以下方式从列表中删除项目:
public void removeItem(String item) {
data.remove(item);
}
若此文档不够详细,可以参考十分钟教你JAVAFX基础入门_哔哩哔哩_bilibili