JavaFX: diferentes diseños en una ListCell personalizada

Estoy creando una aplicación de chat usando JavaFX. Ahora para mostrar mensajes de chat, uso un simpleListView deChatItems con una fábrica de celdas personalizada,ChatCell.

ChatItem podría ser unChatLabel (anuncios del servidor, cambio de estado, etc.) o unChatMessage (mensaje enviado por uno mismo u otras partes). YChatMessage podría extenderse para mostrar diversos contenidos (soloChatTextMessage yChatImageMessage por ahora)

Mi celda personalizada comprueba el tipo / características del elemento y proporciona la vista adecuada en consecuencia.

Me gustaría mostrar una marca de tiempo en cadaChatMessage ver en la esquina inferior derecha de la burbuja de chat. PERO si el texto es solo una línea, quiero que la marca de tiempo se muestre en un espacio adicional a la derecha del texto. (como el chat de WhatsApp o la mayoría de las aplicaciones de chat).

Aquí hay una demostración:

Mi implementación actual (que se muestra en la demostración) utiliza una propiedad booleana enChatMessage eso determina si la vista debe ser multilínea o no. Lo comprueba en cada actualización. También agrego un oyente aListView ancho y llamadarefresh() cuando cambia Todo esto parece excesivo. Y no creo que agregar una propiedad "relacionada con la vista" a un "modelo" sea un buen diseño. Sin mencionar que en la primera actualización del elemento, la marca de tiempo no está en el lugar correcto por alguna razón.

¿Cómo puedo hacer esto correctamente?

ACTUALIZACIÓN 1: Agregué un extractor de propiedades para elListView elementos, y agregó un oyente para elListView ancho que llamaupdateItem en cambios de ancho. Ahora no tengo que llamarrefresh()

Aquí estáChatCell clase:

public class ChatCell extends ListCell<ChatItem> {

    private ChatItemView view;
    private InvalidationListener listViewListener;
    private InvalidationListener listViewWidthListener;

    public ChatCell() {
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        listViewWidthListener = e -> {
            updateItem(getItem(), isEmpty());
        };

        listViewListener = e -> {
            if(getView() == null) return;
            getView().unbindWidth();
            if(listViewProperty().get() != null) {
                getView().bindWidth(listViewProperty().get().widthProperty());
                listViewProperty().get().widthProperty().addListener(listViewWidthListener);
                listViewWidthListener.invalidated(listViewProperty().get().widthProperty());
            }
        };

    }

    public ChatItemView getView() {
        return view;
    }

    private void initChatLabelView() {
        view = new ChatLabelView();
    }

    private void initChatMessageView(ChatMessage message) {
        view = new ChatMessageViewWrapper(message);
    }


    private void resetView() {
        if(view == null) return;
        view.unbindWidth();
        listViewProperty().removeListener(listViewListener);
        if(getListView() != null)
            getListView().widthProperty().removeListener(listViewWidthListener);
        view = null;
    }

    private void newViewCreated() {
        listViewProperty().addListener(listViewListener);
        setGraphic(view);
        listViewListener.invalidated(listViewProperty());
    }

    @Override
    protected void updateItem(ChatItem item, boolean empty) {
        super.updateItem(item, empty);
        if(item == null || empty) {
            setGraphic(null);
            setText(null);
            resetView();
        } else {
            // First-level comparison: Label vs. Message
            if(item.getViewType() == ViewType.LABEL) {
                if(view == null || !(view instanceof ChatLabelView)) {
                    resetView();
                    initChatLabelView();
                    newViewCreated();
                }

            }

            else if(item.getViewType() == ViewType.MESSAGE) {
                ChatMessage message = (ChatMessage) item;
                if(view == null || !(view instanceof ChatMessageViewWrapper)) {
                    resetView();
                    initChatMessageView(message);
                    newViewCreated();
                }
                else {
                    ChatMessageViewWrapper bubble = (ChatMessageViewWrapper) view;
                    // Second-level comparison: Self-message vs. other user message,
                    // Text vs. Image (or file), multiline vs. oneline 
                    if(  (bubble.getContentType() != message.getContentType())
                            || (bubble.isSelf() ^ message.isSelf()) 
                            || (bubble.isMultiline() ^ message.isMultiline())) {
                        resetView();
                        initChatMessageView(message);
                        newViewCreated();
                    }

                }

            }

            else {
                return;
            }

            view.setItem(item);
        }
    }

    @Override
    public Orientation getContentBias() {
        return Orientation.HORIZONTAL;
    }

}

Respuestas a la pregunta(0)

Su respuesta a la pregunta