【问题标题】:JavaFX ObservableList change not updating ListViewJavaFX ObservableList 更改未更新 ListView
【发布时间】:2016-03-02 20:28:19
【问题描述】:

所以我正在为服务器控制器创建一个带有 JavaFX 的 UI,这并不重要,重要的是 server.getClients();返回 IClient 的 ArrayList。

我希望在 ListView 中显示这些客户端(它们由 IP 表示,但再一次,这似乎不相关)。但是,客户端可以在任何给定时间点连接,当这种情况发生时,它们会被添加到服务器的 IClient ArrayList 中。更新此列表时,我希望 ListView 刷新并显示新客户端。出于某种原因,我根本无法让它工作。

我对 JavaFX 很陌生,我想我可能正在监督一些事情。 如果这是重复或明显的,我很抱歉,在过去的几天里我已经搜索了很长时间,但我可能忽略了一个解决方案。

以下代码是我的 FXMLController 用于 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;
    }
}

【问题讨论】:

  • 您是否尝试过在 Stack Overflow 上搜索该主题?几乎可以肯定之前有人问过。
  • 我搜索了 Stack Overflow,确实找到了建议的重复项,但我无法让它工作。
  • 显示将新客户端添加到列表的代码。你名单上的听众真的什么都没做。
  • @James_D 我添加了代码并解释了一点,我试图让问题保持简单以避免混淆,但更好的解释并没有什么坏处。也感谢您澄清我的听众的无用

标签: java listview javafx


【解决方案1】:

使用FXCollections.observableList(...) 创建您的可观察列表作为ServerService 中基础列表的包装器。这个返回的可观察列表只是包装了底层列表,所以它总是包含与底层列表相同的元素。但是,如文档中所述:

直接对底层列表进行的变异操作不会报告给任何包装它的 ObservableList 的观察者。

当客户端在服务器服务中注册或注销时,您将它们添加到基础列表中。由于底层列表不是可观察列表,因此不会触发任何通知,因此 ListView 不知道自己刷新。

一种可能的解决方案是在ServerService 中使用ObservableList

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&lt;IClient&gt; 简单地表示这些。这看起来像:

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);
}

【讨论】:

  • 哇,好的,谢谢!很好的解释,我现在真的明白了。我会检查解决方案,看看哪个有效
  • 最后的建议很有效,感谢您的解释和回答!
  • 查看this article获取一些相关资料。
猜你喜欢
  • 2012-12-04
  • 1970-01-01
  • 2023-04-03
  • 2019-10-25
  • 1970-01-01
  • 2020-11-03
  • 2017-03-06
  • 1970-01-01
相关资源
最近更新 更多