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
deChatItem
s 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;
}
}