JavaFX - Verschiedene Layouts in einer benutzerdefinierten ListCell
Ich erstelle eine Chat-App mit JavaFX. Jetzt für die Anzeige von Chat-Nachrichten verwende ich ein einfachesListView
vonChatItem
s mit einer benutzerdefinierten Zellenfactory,ChatCell
.
ChatItem
könnte ein @ seChatLabel
(Serverankündigungen, Statusänderung usw.) oder einChatMessage
(Nachricht von sich selbst oder anderen Parteien gesendet). UndChatMessage
kann erweitert werden, um verschiedene Inhalte anzuzeigen (nurChatTextMessage
undChatImageMessage
zur Zeit
Meine benutzerdefinierte Zelle überprüft den Typ / die Merkmale des Elements und bietet die entsprechende Ansicht.
Ich möchte auf jedem @ einen Zeitstempel anzeigChatMessage
view in der rechten unteren Ecke der Chat-Blase. ABER wenn der Text nur aus einer Zeile besteht, soll der Zeitstempel in einem zusätzlichen Leerzeichen rechts vom Text angezeigt werden. (ein bisschen wie WhatsApp-Chat oder die meisten Chat-Apps).
Hier ist eine Demo:
Meine aktuelle Implementierung (in der Demo gezeigt) verwendet eine boolesche Eigenschaft inChatMessage
das bestimmt, ob die Ansicht mehrzeilig sein soll oder nicht. Es überprüft es bei jedem Update. Ich füge @ auch einen Listener hinListView
width und callrefresh()
wenn es sich ändert. All dies scheint übertrieben. Und ich denke nicht, dass das Hinzufügen einer "ansichtsbezogenen" Eigenschaft zu einem "Modell" ein gutes Design ist. Ganz zu schweigen davon, dass der Zeitstempel bei der ersten Artikelaktualisierung aus irgendeinem Grund nicht an der richtigen Stelle ist.
Wie kann ich das richtig machen?
UPDATE 1: Ich habe einen Eigenschaftsextraktor für das @ hinzugefüListView
items und fügte einen Listener für das @ hinListView
width die @ aufruupdateItem
bei Breitenänderungen. Jetzt muss ich nicht mehr @ anrufrefresh()
Hier istChatCell
Klasse:
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;
}
}