JavaFX - Различные макеты в пользовательском ListCell
Я создаю приложение чата с использованием JavaFX. Теперь для отображения сообщений чата я использую простуюListView
изChatItem
с фабрикой пользовательских ячеек,ChatCell
.
ChatItem
может бытьChatLabel
(серверные объявления, изменение статуса и т. д.) илиChatMessage
(сообщение отправлено самим или другими сторонами). А такжеChatMessage
может быть расширен для показа различного контента (простоChatTextMessage
а такжеChatImageMessage
теперь)
Моя настраиваемая ячейка проверяет тип / характеристики элемента и соответственно отображает правильный вид.
Я хотел бы показать отметку времени на каждомChatMessage
вид в правом нижнем углу пузыря чата. НО, если текст состоит только из одной строки, я хочу, чтобы отметка времени отображалась на дополнительном месте справа от текста. (вроде чата WhatsApp или большинства приложений чата).
Вот демо:
Моя текущая реализация (показанная в демонстрации) использует логическое свойство вChatMessage
это определяет, должно ли представление быть многострочным или нет. Он проверяет это при каждом обновлении. Я также добавляю слушателяListView
ширина и вызовrefresh()
когда это изменится. Все это кажется чрезмерным. И я не думаю, что добавление «связанных с видом» свойств к «модели» - это хороший дизайн. Не говоря уже о том, что при первом обновлении элемента временная метка по какой-то причине находится не в правильном месте.
Как я могу сделать это правильно?
ОБНОВЛЕНИЕ 1: Я добавил экстрактор свойств дляListView
элементы, и добавил слушателя дляListView
ширина, которая вызываетupdateItem
по ширине меняется. Теперь мне не нужно звонитьrefresh()
ВотChatCell
учебный класс:
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;
}
}