所以我用JavaFX为服务器控制器创建一个UI,它没关系,重要的是server.getClients();返回IClients的ArrayList。
我希望在ListView中显示这些客户端(它们由IP表示,但再一次,这似乎并不相关)。但是,客户端可以在任何给定的时间点连接,当发生这种情况时,它们会被添加到服务器的IClient ArrayList中。更新此列表后,我希望ListView刷新并显示新客户端。出于某种原因,我根本无法让这个工作。
我是JavaFX的新手,我想我可能会监督一些事情。 我非常抱歉,如果这是重复或明显的,我在过去几天里搜索了很长时间,但我可能忽略了一个解决方案。
以下代码是我的FXFLController for JavaFX应用程序的缩写版本:
/*imports*/
public class FXMLController implements Initializable {//serverUI.FXMLController
@FXML private ListView clientListView;
/*some more (irrelevant) code*/
private IServer server;
/*some more (irrelevant) code*/
private ObservableList<IClient> serverClientsObservableList;
@Override
public void initialize(URL url, ResourceBundle rb) {
System.out.println("initialization...");
/*some more (irrelevant) code*/
//the server was started here
// FXML Controls
initClientListView();
/*some more (irrelevant) code*/
System.out.println("initialized");
}
/*some more (irrelevant) code*/
private void initClientListView() {
System.out.println("clientListView");
serverClientsObservableList = FXCollections.observableList(server.getClients());
serverClientsObservableList.addListener(new ListChangeListener<IClient>() {
@Override
public void onChanged(ListChangeListener.Change<? extends IClient> change) {
System.out.println("list change detected");
//is any of the followin three lines really necessary to update the ListView content?
serverClientsObservableList.setAll(server.getClients());
clientListView.setItems(null);
clientListView.setItems(serverClientsObservableList);
}
});
clientListView.setItems(serverClientsObservableList);
}
/*some more (irrelevant) code*/
}
编辑:
当IClients中的某些内容发生变化时,我不想刷新ListView,它们没有任何变化。我想在将新的IClient添加到服务器的客户端列表时刷新ListView。 ListView应该显示NEW IClient
EDIT2:
根据建议的副本,我尝试了以下内容,但是我并不真正理解它在做什么以及它是如何工作的。这没有解决问题,当我尝试创建新的Observable []
时,它给了我一个错误Callback<IClient, Observable[]> extractor = new Callback<IClient, Observable[]>() {
@Override
public Observable[] call(IClient c) {
return new Observable[] {c.getNameProperty()};
}
};
ObservableList<IClient> clientOList = FXCollections.observableArrayList(extractor);
此外:我将客户端添加到服务器的代码。 简而言之,这是一个我们必须以相反的方式使用RMI的任务,服务器命令客户端。客户将自己注册到服务器列表以及它们被添加到IClient列表的位置。
package serviceImplementation;
import commandService.ICommand;
import commandServiceImplementation.CommandResult;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import service.IClient;
import service.IServer;
public class ServerService extends UnicastRemoteObject implements IServer {
private ArrayList<IClient> clients;
public ServerService() throws RemoteException {
clients = new ArrayList<>();
}
@Override
public boolean register(IClient client) throws RemoteException {
if(!clients.contains(client)) {
clients.add(client);
return true;
}
return false;
}
@Override
public boolean unRegister(IClient client) throws RemoteException {
if(clients.contains(client)) {
clients.remove(client);
return true;
}
return false;
}
@Override
public String ping() throws RemoteException {
long arrival = System.currentTimeMillis();
System.out.println("Got pinged at [" + arrival + "]");
return ("server ponged [" + arrival + "]");
}
@Override
public CommandResult sendCommand(ICommand command, IClient targetClient) throws RemoteException {
if(clients.contains(targetClient)) {
return clients.get(clients.indexOf(targetClient)).executeCommand(command);
}
return null;
}
@Override
public ArrayList<IClient> getClients() {
return clients;
}
}
答案 0 :(得分:3)
使用FXCollections.observableList(...)
创建您的可观察列表作为ServerService
中基础列表的包装。由此返回的可观察列表仅包装基础列表,因此它始终包含与基础列表相同的元素。但是,如文档中所述:
直接对基础列表进行的变异操作不会报告给包装它的任何ObservableList的观察者。
在服务器服务中注册或取消注册客户端时,将它们添加到基础列表中。由于基础列表不是可观察列表,因此不会触发任何通知,因此ListView
无法自行刷新。
一种可能的解决方案可能是在ObservableList
中使用ServerService
:
public class ServerService extends UnicastRemoteObject implements IServer {
private ObservableList<IClient> clients;
public ServerService() throws RemoteException {
clients = FXCollections.observableArrayList();
}
// ...
@Override
public ObservableList<IClient> getClients() {
return clients;
}
}
然后你做
private void initClientListView() {
clientListView.setItems(server.getClients());
}
请注意,这会将您的ServerService
与JavaFX API相关联;这可能并不算太糟糕,因为JavaFX Collections API根本不依赖于任何UI元素。
但是,如果您的客户在后台线程(即不在FX应用程序线程上)注册/取消注册,则上述代码将不起作用,这几乎肯定是这种情况。因此,您需要进行以下更改才能使其正常工作:
@Override
public boolean register(IClient client) throws RemoteException {
FutureTask<Boolean> register = new FutureTask<>(() ->
if(!clients.contains(client)) {
clients.add(client);
return true;
}
return false;
);
Platform.runLater(register);
return register.get();
}
@Override
public boolean unRegister(IClient client) throws RemoteException {
FutureTask<Boolean> unRegister = new FutureTask<>(() ->
if(clients.contains(client)) {
clients.remove(client);
return true;
}
return false;
);
Platform.runLater(unRegister);
return unRegister.get();
}
现在你的ServerService
对JavaFX有更强的依赖性,因为它假设FX应用程序线程正在运行。你没有对如何使用它做任何规范,但是你很可能不希望这种耦合。
另一种方法是支持ServerService
中的回调。您可以使用Consumer<IClient>
简单地表示这些。这看起来像:
public class ServerService extends UnicastRemoteObject implements IServer {
private ArrayList<IClient> clients;
private Consumer<IClient> registerCallback = client -> {} ;
private Consumer<IClient> unregisterCallback = client -> {} ;
public ServerService() throws RemoteException {
clients = new ArrayList<>();
}
public void setRegisterCallback(Consumer<IClient> registerCallback) {
this.registerCallback = registerCallback ;
}
public void setUnregisterCallback(Consumer<IClient> unregisterCallback) {
this.unregisterCallback = unregisterCallback ;
}
@Override
public boolean register(IClient client) throws RemoteException {
if(!clients.contains(client)) {
clients.add(client);
registerCallback.accept(client);
return true;
}
return false;
}
@Override
public boolean unRegister(IClient client) throws RemoteException {
if(clients.contains(client)) {
clients.remove(client);
unregisterCallback.accept(client);
return true;
}
return false;
}
@Override
public String ping() throws RemoteException {
long arrival = System.currentTimeMillis();
System.out.println("Got pinged at [" + arrival + "]");
return ("server ponged [" + arrival + "]");
}
@Override
public CommandResult sendCommand(ICommand command, IClient targetClient) throws RemoteException {
if(clients.contains(targetClient)) {
return clients.get(clients.indexOf(targetClient)).executeCommand(command);
}
return null;
}
@Override
public ArrayList<IClient> getClients() {
return clients;
}
}
现在在您的UI代码中
private void initClientListView() {
System.out.println("clientListView");
serverClientsObservableList = FXCollections.observableArrayList(server.getClients());
server.setRegisterCallback(client -> Platform.runLater(() ->
serverClientsObservableList.add(client)));
server.setUnregisterCallback(client -> Platform.runLater(() ->
serverClientsObservableList.remove(client)));
clientListView.setItems(serverClientsObservableList);
}