Я создаю приложение чата и для отображения сообщений использую представление переработчика. Самые новые сообщения отображаются внизу. Пользователь прокручивает страницу, чтобы увидеть больше сообщений.
Когда загружается экран чата, представление не начинается с самого низа, а самое новое сообщение не отображается. Пользователь должен прокрутить несколько строк вниз, чтобы увидеть самое новое сообщение. Это плохой интерфейс, и самое последнее сообщение должно быть видно в конце / нижней части экрана.
Я использую setReverseLayout(true)
и setStackFromEnd(false)
, и я искал в Интернете похожие проблемы, но не повезло. На данный момент я устанавливаю позицию прокрутки на 0 сразу после настройки представления переработчика с задержкой, но это не всегда работает, и это нервно.
Если я настрою утилиту обычного просмотра (без использования setReverseLayout
и setStackFromEnd
), самое новое сообщение загружается вверху каждый раз идеально, как и должно.
Вот код для запуска представления переработчика:
RecyclerView recyclerView = findViewById(R.id.recyclerViewMessageRoom);
adapter = new RA_MessageRoom(this, userFirebaseUid, messagesGroupedByDate, messageUsers);
recyclerView.setAdapter(adapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(false);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
// -- Workaround with delay - still doesn't completely work
new Handler().postDelayed(() -> {
recyclerView.scrollToPosition(0);
}, 200);
Всем, кто сталкивался с этой проблемой и знает, как ее решить, поделитесь! Спасибо.
РЕДАКТИРОВАТЬ (6 ИЮЛЯ 2019):
Вот код адаптера утилизатора. В качестве напоминания, если я уберу настройки обратного макета, он отлично работает.
RA_MessageRoom:
public class RA_MessageRoom extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = "RA_MessageRoom";
private static final int TYPE_USER = 1;
private static final int TYPE_PARTICIPANT = 2;
private Context context;
private String userFirebaseUid;
private List<MessagesDateGrouper> messagesGroupedByDate;
private HashSet<MessagesUserModel> messageUsers;
public RA_MessageRoom(Context context, String userFirebaseUid, List<MessagesDateGrouper> messagesGroupedByDate, HashSet<MessagesUserModel> messageUsers) {
this.context = context;
this.userFirebaseUid = userFirebaseUid;
this.messagesGroupedByDate = messagesGroupedByDate;
this.messageUsers = messageUsers;
}
@Override
public int getItemViewType(int position) {
if (messagesGroupedByDate.get(position).getViewType() == MessagesDateGrouper.TYPE_CHAT) {
MessageChatItem message = (MessageChatItem) messagesGroupedByDate.get(position);
if (message.getMessages().getSenderFirebaseUid().equals(userFirebaseUid)) {
return TYPE_USER;
} else {
return TYPE_PARTICIPANT;
}
} else {
return messagesGroupedByDate.get(position).getViewType();
}
}
@Override
public int getItemCount() {
return messagesGroupedByDate != null ? messagesGroupedByDate.size() : 0;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final RecyclerView.ViewHolder holder;
View view;
switch (viewType) {
case MessagesDateGrouper.TYPE_DATE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_message_room_separator, parent, false);
holder = new MessageRoomDateVH(view);
break;
case TYPE_USER:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_message_room_user, parent, false);
holder = new MessageRoomUserVH(view);
break;
case TYPE_PARTICIPANT:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_message_room_participant, parent, false);;
holder = new MessageRoomParticipantVH(view);
break;
default:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_message_room_user, parent, false);;
holder = new MessageRoomUserVH(view);
break;
}
return holder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof MessageRoomDateVH) {
MessageDateItem date = (MessageDateItem) messagesGroupedByDate.get(position);
((MessageRoomDateVH)holder).date.setText(date.getDate());
} else if (holder instanceof MessageRoomParticipantVH) {
MessageRoomParticipantVH view = (MessageRoomParticipantVH) holder;
MessageChatItem messageItem = (MessageChatItem) messagesGroupedByDate.get(position);
MessagesModel message = messageItem.getMessages();
view.name.setText(context.getString(R.string.unknown));
view.profileImage.setImageResource(R.drawable.default_profile_image_grey);
for (MessagesUserModel user: messageUsers) {
if (user.getFirebaseId().equals(message.getSenderFirebaseUid())) {
int fallbackImage;
if (user.getMerchant() == null || !user.getMerchant()) {
fallbackImage = R.drawable.default_profile_image_grey;
} else {
fallbackImage = R.drawable.store_profile;
}
GlideApp.with(context)
.load(user.getPhotoThumbUrl())
.placeholder(R.drawable.placeholder)
.fallback(fallbackImage)
.into(view.profileImage);
view.name.setText(user.getName());
break;
}
}
if (message.getImageUrl() != null && !message.getImageUrl().equals("") ) {
view.image.setVisibility(View.VISIBLE);
view.imageSpinner.setVisibility(View.VISIBLE);
view.chat.setVisibility(View.GONE);
view.image.setClipToOutline(true);
GlideApp.with(context)
.load(message.getImageUrl())
.placeholder(R.drawable.placeholder_message)
.fallback(R.drawable.placeholder_message)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
view.imageSpinner.setVisibility(View.GONE);
return false;
}
})
.into(view.image);
} else {
view.chat.setVisibility(View.VISIBLE);
view.image.setVisibility(View.GONE);
view.imageSpinner.setVisibility(View.GONE);
view.chat.setText(message.getMessageText());
}
String time = DateFormatService.messageRoomParseDateToTimeString(message.getDate());
view.date.setText(time);
} else if (holder instanceof MessageRoomUserVH){
MessageRoomUserVH view = (MessageRoomUserVH) holder;
MessageChatItem messageItem = (MessageChatItem) messagesGroupedByDate.get(position);
MessagesModel message = messageItem.getMessages();
if (message.getImageUrl() != null && !message.getImageUrl().equals("") ) {
view.image.setVisibility(View.VISIBLE);
view.imageSpinner.setVisibility(View.VISIBLE);
view.chat.setVisibility(View.GONE);
view.image.setClipToOutline(true);
GlideApp.with(context)
.load(message.getImageUrl())
.placeholder(R.drawable.placeholder_message)
.fallback(R.drawable.placeholder_message)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
view.imageSpinner.setVisibility(View.GONE);
return false;
}
})
.into(view.image);
} else {
view.chat.setVisibility(View.VISIBLE);
view.image.setVisibility(View.GONE);
view.imageSpinner.setVisibility(View.GONE);
view.chat.setText(message.getMessageText());
}
String time = DateFormatService.messageRoomParseDateToTimeString(message.getDate());
view.date.setText(time);
}
}
public class MessageRoomDateVH extends RecyclerView.ViewHolder {
TextView date;
public MessageRoomDateVH(@NonNull View itemView) {
super(itemView);
date = itemView.findViewById(R.id.textMessageRoomDateSection);
}
}
public class MessageRoomParticipantVH extends RecyclerView.ViewHolder {
ImageView profileImage;
TextView name;
TextView chat;
ImageView image;
TextView date;
ProgressBar imageSpinner;
public MessageRoomParticipantVH(@NonNull View itemView) {
super(itemView);
profileImage = itemView.findViewById(R.id.imageMessageRoomParticipantProfile);
name = itemView.findViewById(R.id.textMessageRoomParticipantName);
chat = itemView.findViewById(R.id.textMessageRoomParticipantChat);
image = itemView.findViewById(R.id.imageMessageRoomParticipantImage);
date = itemView.findViewById(R.id.textMessageRoomParticipantDate);
imageSpinner = itemView.findViewById(R.id.progressBarMessageRoomParticipantImage);
}
}
public class MessageRoomUserVH extends RecyclerView.ViewHolder {
TextView chat;
ImageView image;
TextView date;
ProgressBar imageSpinner;
public MessageRoomUserVH(@NonNull View itemView) {
super(itemView);
chat = itemView.findViewById(R.id.textMessageRoomUserChat);
image = itemView.findViewById(R.id.imageMessageRoomUserImage);
date = itemView.findViewById(R.id.textMessageRoomUserDate);
imageSpinner = itemView.findViewById(R.id.progressBarMessageRoomUserImage);
}
}
}
РЕДАКТИРОВАТЬ:
Хотя предпочтение было в том, чтобы для stackFromEnd было установлено значение false, в итоге я изменил stackFromEnd со значения false на true, устранил задержку и продолжал устанавливать позицию прокрутки, как это было принято в принятом ответе, для решения этой проблемы.
Обновлен код:
RecyclerView recyclerView = findViewById(R.id.recyclerViewMessageRoom);
adapter = new RA_MessageRoom(this, userFirebaseUid, messagesGroupedByDate, messageUsers);
recyclerView.setAdapter(adapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.scrollToPosition(0);
Спасибо за помощь!