안녕하세요 ~ ^^
오늘은 좀 빨리 왔습니다 ㅎㅎ
저는 채팅방에 들어올 때마다! 최신 메세지 30개를 불러오고, 최상단으로 recyclerview를 스크롤 하면 그 상위의 30개 메세지를 더 불러오는 형식으로 진행했었는데요 ㅋㅋ
첨부터 다 불러오고 db에 저장하는 형식으로 할 껄 그랬습니다 ~ ㅋㅋ
그치만 페이지네이션 관련해서도 설정하느랴 애먹었으니
페이지네이션 관련 해서도 집중해서 봐주셔도 좋을 것 같습니다!
Message message = new Message(content, ServerValue.TIMESTAMP, userUid, userName);
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
databaseReference.child("channels/MainChattingRoom/messages").push().setValue(message);
저는 메세지를 push방식으로 저장했습니다. (이전글 참고)

RealtimeDB관련해서 공식문서에 목록 항목이 시간순으로 자동 정렬 된다고 써 있으므로 전 키값을 기준으로 메세지를 불러올 것입니다!
Query query = FirebaseDatabase.getInstance().getReference("channels")
.child("MainChattingRoom").child("messages")
.orderByKey()
.limitToLast(ITEM_LOAD_COUNT);
키값으로 정렬한 후, 마지막 ITEM_LOAD_COUNT 개수만큼 불러올 수 있는 쿼리를 작성해줍니다. 전 30으로 설정했어요.
제일 마시막 메세지부터 하나씩 순서대로 불러올 수 있습니다.
query.addChildEventListener(new ChildEventListener() {
int i = 0;
@Override
public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
flag = true;
Message message = snapshot.getValue(Message.class);
int msgSize = messageArrayList.size();
String xDate;
String tDate;
if(msgSize == 0){
xDate = "";
}else {
xDate = getTimestampToDate(messageArrayList.get(msgSize-1).getDate());
}
tDate = getTimestampToDate(message.getDate());
if(!xDate.equals(tDate)){
Message message1 = new Message(message.getContent(), message.getDate(), message.getSenderId(), message.getSenderName());
message1.setViewType(2);
messageArrayList.add(message1);
}
if (Objects.equals(message.getSenderId(), userUid)) {
message.setViewType(0);
messageArrayList.add(message);
} else{
message.setViewType(1);
messageArrayList.add(message);
}
if(i==0){
first_node = snapshot.getKey();
}
i=i+1;
messageAdapter.submitData(messageArrayList);
recyclerView.scrollToPosition(messageArrayList.size()-1);
}
@Override
public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {}
@Override
public void onChildRemoved(@NonNull DataSnapshot snapshot) {}
@Override
public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {}
@Override
public void onCancelled(@NonNull DatabaseError error) {}
});
이 쿼리에 addChildEventListener 를 설정함으로써, 새로 추가되는 메세지 또한 리사이클러뷰에 추가될 수 있도록 설정했습니다. (하위 노드에 대한 변경 사항을 감지)
xDate에는 이전 메세지의 시간 값을 저장하고, tDate에는 현재 메세지의 시간 값을 저장해줍니다.
private static String getTimestampToDate(Object timestamp) {
Date date = new Date((Long) timestamp);
SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy.MM.dd (E)");
sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
String formattedDate = sdf.format(date);
return formattedDate;
}
년.월,일 (요일) 형식으로 타임스탬프 값을 바꿔서 비교함으로써, 날짜가 변경됐는지 확인했습니다.
이전 메세지의 시간 값과 현재 메세지의 값이 다를 경우, messageArrayList에 viewType을 2로 설정해서 값을 넣어줬습니다.
그 이후, 현재 메세지를 넣어줍니다. uid를 비교해서 내가 보낸 메세지인지, 남이 보낸 메세지인지 구분했습니다.
first_node = snapshot.getKey();
이렇게 첫번째 키를 저장한 것은 페이지네이션 부분에서 활용됩니다.
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// 최상단에 있을 때 메세지 불러오기
if(!recyclerView.canScrollVertically(-1) && flag){
getMessages();
// 최하단에 있을 때 메세지 하단으로 내리는 버튼 삭제
} else if (!recyclerView.canScrollVertically(1) && flag) {
btnDown.setVisibility(View.GONE);
} else{
// 최하단에 있을 때 메세지 하단으로 내리는 버튼 삭제
btnDown.setVisibility(View.VISIBLE);
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
currentitems = recyclerView.getChildCount();
tottalitems = messageAdapter.getItemCount();
scrolledoutitems = layoutManager.findFirstCompletelyVisibleItemPosition();
}
});
private void getMessages() {
Query query;
// 저장된 first_node 값을 기준으로 ITEM_LOAD_COUNT 만큼 불러옴
query = FirebaseDatabase.getInstance().getReference("channels")
.child("MainChattingRoom").child("messages")
.orderByKey()
.endBefore(first_node)
.limitToLast(ITEM_LOAD_COUNT);
query.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
// 이 쿼리에 값이 있을 때만 동작
if(snapshot.hasChildren()){
ArrayList <Message> newMsgArray = new ArrayList<>();
int i = 0;
for(DataSnapshot dataSnapshot : snapshot.getChildren()){
Message message = dataSnapshot.getValue(Message.class);
int msgSize = newMsgArray.size();
String xDate;
String nDate;
String currentDate;
xDate = getTimestampToDate(messageArrayList.get(0).getDate());
currentDate = getTimestampToDate(message.getDate());
if (messageArrayList.get(0).getViewType() == 2 && xDate.equals(currentDate)){
messageArrayList.remove(0);
messageAdapter.submitData(messageArrayList);
messageAdapter.notifyDataSetChanged();
}
if(msgSize == 0){
nDate = "";
}else {
nDate = getTimestampToDate(newMsgArray.get(msgSize - 1).getDate());
}
if(!nDate.equals(currentDate)){
Message message1 = new Message(message.getContent(), message.getDate(), message.getSenderId(), message.getSenderName());
message1.setViewType(2);
newMsgArray.add(message1);
}
if (Objects.equals(message.getSenderId(), userUid)) {
message.setViewType(0);
newMsgArray.add(message);
} else{
message.setViewType(1);
newMsgArray.add(message);
}
if (i == 0){
x_first_node = first_node;
first_node = dataSnapshot.getKey();
}
i=i+1;
}
// 첫번째 노드까지 다 불러왔을 경우엔 더 불러오지 않음
if (first_node.equals(first_key) && isMax){
} else if (!Objects.equals(x_first_node, first_node)){
messageArrayList = (ArrayList<Message>) Stream.concat(newMsgArray.stream(), messageArrayList.stream()).collect(Collectors.toList());
messageAdapter.submitData(messageArrayList);
messageAdapter.notifyDataSetChanged();
layoutManager.scrollToPositionWithOffset(ITEM_LOAD_COUNT,0);
}
if (first_node.equals(first_key)){
isMax = true;
}
}else{
isMaxData = true;
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {}
});
}
페이지네이션 관련 코드입니다.
scrollChangeListener가 끝도없이 호출돼서 .. 메세지 추가를 주체할 수 없었던 기억이 납니다..
맨 위 메세지의 키 값을 잘 저장하고,
모든 메세지를 불러왔다면, 더는 추가하지 않는 것이
중요한것 같습니다.
화이팅입니다!
설명이 부족하다고 느껴지신다면 댓글 남겨주세요.

'Android > Firebase' 카테고리의 다른 글
| [JAVA] Android 채팅방 UI 만들기 (4) | 2024.01.03 |
|---|---|
| [JAVA] Firebase로 채팅앱 만들기(1) - RealtimeDB에 push하는 법 (0) | 2023.04.01 |