JavaFX ListView cell factory item rendering complete event

Task: Check if item is in visible region of ListView.

Solution: I have JavaFX ListView containing items to render. In order to figure which items are in visible region of ListView I implemented cell factory which calculates number of items being displayed to user.

So, basically what I need to do:
1. Add item
2. Check if it is visible in ListView.

The problem: In order to calculate items, item adding thread (calling thread) must wait for cell factory to complete item adding operation and rendering it. However, I don't know how to implement it as calling thread doesn't know when JavaFX UI thread finishes rendering using internal Quantum Toolkit mechanics. Cell factory items are being rendered in separate threads inside JavaFX which is not accessible to synchronize with.

Adding rough calling thread delay solves an issue which clearly indicates threads synchronizing issue but I need more elegant and clear solution.

public class MessengerServiceControl implements Initializable {
...
private TrackingListCellFactory<MessageElementControl> messengerOutputWindowListViewCellFactory;
...

    //Calling (message processing) thread method which inserts ListView item
    public void receiveMessage(final MessengerMessageData messengerMessageData) { 
        //Calling MessengerServiceControl model method to insert items in ListView using JavaFX UI thread
        MainFrameWindow.runOnUIThread(() -> {
                byte[] identityImageByteArray = messengerPlugin.getApplicationManager().getPeerImage(messengerMessageData.getMessageSenderPeerAdvertisement());
                MessageElementControl messageElementControl = model.createMessage(messengerMessageData, false);
                Image identityImage = new Image(new ByteArrayInputStream(identityImageByteArray != null ? identityImageByteArray : MainFrameWindow.getDefaultUserImageByteArray()));
                messageElementControl.setIdentityImage(identityImage); 

               //Check if item was added (in the same thread)
               if (!getMessageElementControlVisibility(messengerMessageData)) {
                    int newMessagesCount = getNewMessagesCount().get();
                    getNewMessagesCount().set(++newMessagesCount);                        

            }, false);
        }

    boolean getMessageElementControlVisibility(final MessengerMessageData messengerMessageData) {                
            return messengerOutputWindowListViewCellFactory.getItemVisibility(messengerMessageData);
        }  



    //Cell factory class which is responsible for items rendering:
    private static class TrackingListCellFactory<T extends MessageElementControl> implements Callback<ListView<T>, ListCell<T>> {
            //Items which have cells visible to the user
            private final ObservableSet<T> visibleItems = FXCollections.observableSet();

            TrackingListCellFactory() {    
            }

            boolean getItemVisibility(final MessengerMessageData messengerMessageData) {
                synchronized (this) {
                    Optional<T> messageElementControlOptional = visibleItems.stream().filter((item) -> {
                        return item.getMessageData().getMessageCreatedDate().isEqual(messengerMessageData.getMessageCreatedDate());
                    }).findFirst();

                    return messageElementControlOptional.isPresent();
                }
            } 

            @Override
            public ListCell<T> call(ListView<T> param) {
                //Create cell that displays content
                ListCell<T> cell = new ListCell<T>() {
                    @Override
                    protected void updateItem(T item, boolean empty) {
                        super.updateItem(item, empty);

                        if (!empty && item != null) {
                            setGraphic(item);
                        }
                    }
                };            

                //Add and remove item when cell is reused for different item
                cell.itemProperty().addListener((observable, oldItem, newItem) -> { 
                    synchronized (TrackingListCellFactory.this) {
                        if (oldItem != null) {
                            visibleItems.remove(oldItem);
                        }

                        if (newItem != null) {
                            visibleItems.add(newItem);
                        }                
                    }
                });

                //Update set when bounds of item change
                ChangeListener<Object> boundsChangeHandler = (observable, oldValue, newValue) -> {
                    synchronized (TrackingListCellFactory.this) {
                        T item = cell.getItem();

                        if (item != null) {
                            visibleItems.add(item);
                        }
                    }
                };

                //Must update either if cell changes bounds, or if cell moves within scene (e.g.by scrolling):
                cell.boundsInLocalProperty().addListener(boundsChangeHandler);
                cell.localToSceneTransformProperty().addListener(boundsChangeHandler);

                return cell;
            }
        }
}

Also tried to bind with added item's scneProperty(). Does not work either.

messageElementControl.sceneProperty().addListener((obs, oldScene, newScene) -> {
                if (newScene != null) {
                    if (!getMessageElementControlVisibility(messengerMessageData)) {
                        int newMessagesCount = getNewMessagesCount().get();
                        getNewMessagesCount().set(++newMessagesCount);
                    }
                }
            });