JavaFX - Diferentes layouts em um ListCell personalizado
Estou construindo um aplicativo de bate-papo usando JavaFX. Agora, para exibir mensagens de bate-papo, eu uso um simplesListView
doChatItem
s com uma fábrica de células personalizada,ChatCell
.
ChatItem
poderia ser umChatLabel
(anúncios do servidor, alteração de status etc.) ou umChatMessage
(mensagem enviada por si ou por terceiros). EChatMessage
pode ser estendido para mostrar vários conteúdos (apenasChatTextMessage
eChatImageMessage
para agora)
Minha célula personalizada verifica o tipo / características do item e fornece a visualização adequada de acordo.
Eu gostaria de mostrar um carimbo de data / hora em cadaChatMessage
veja no canto inferior direito do balão de bate-papo. MAS, se o texto for apenas uma linha, quero que o carimbo de data e hora seja exibido em um espaço extra à direita do texto. (como o WhatsApp ou a maioria dos aplicativos de bate-papo).
Aqui está uma demonstração:
Minha implementação atual (mostrada na demonstração) usa uma propriedade booleana emChatMessage
que determina se a visualização deve ser multilinha ou não. Ele verifica todas as atualizações. Também adiciono um ouvinte aListView
largura e chamadarefresh()
quando isso muda. Tudo isso parece excessivo. E não acho que adicionar uma propriedade "relacionada à exibição" a um "modelo" seja um bom design. Sem mencionar que, na primeira atualização do item, o carimbo de data e hora não está no local correto por algum motivo.
Como posso fazer isso corretamente?
ATUALIZAÇÃO 1: Eu adicionei um extrator de propriedade para oListView
itens e adicionou um ouvinte para oListView
largura que chamaupdateItem
na largura muda. Agora não preciso ligarrefresh()
Aqui estáChatCell
classe:
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;
}
}