英文:
Why does recyclerview ScrollToBottom is not working efficiently?
问题
我彻底搜索并尝试实现了不同的事情,但似乎不可能,
要求:
在我的聊天应用中,当我打开特定用户的聊天界面时,
- 它应该滚动到底部,以便用户可以看到最后一条消息/对话
- 当有任何新消息/新项目添加时,它应该滚动到底部
请注意,如果我使用以下方法,则我的第一个要求无法实现
private void loadMessages() {
try {
DatabaseReference messageReference = rootReference.child(MESSAGES).child(currentUId).child(chatUser);
Query query = messageReference.orderByChild(TIME);
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
messagesList.clear();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
try {
Messages messages = ds.getValue(Messages.class);
messagesList.add(messages);
setAdapter();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
databaseError.getMessage();
}
};
query.addValueEventListener(valueEventListener);
} catch (Exception e) {
e.printStackTrace();
}
}
private void setAdapter() {
try {
if (mAdapter != null) {
mLayoutManager.smoothScrollToPosition(binding.recyclerViewLayout.recyclerView, null, messagesList.size());
mAdapter.notifyDataSetChanged();
} else {
mAdapter = new MessageAdapter(this, messagesList);
mLayoutManager.setStackFromEnd(false);
mLayoutManager.setSmoothScrollbarEnabled(true);
binding.recyclerViewLayout.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerViewLayout.recyclerView.setAdapter(mAdapter);
binding.recyclerViewLayout.recyclerView.setVisibility(View.VISIBLE);
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
如果我使用以下方法,则我的第二个要求无法实现!
private void loadMessages() {
try {
DatabaseReference messageReference = rootReference.child(MESSAGES).child(currentUId).child(chatUser);
Query query = messageReference.orderByChild(TIME);
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
messagesList.clear();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
try {
Messages messages = ds.getValue(Messages.class);
messagesList.add(messages);
Comparator<Messages> compare = Collections.reverseOrder();
Collections.sort(messagesList, compare);
setAdapter();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
databaseError.getMessage();
}
};
query.addValueEventListener(valueEventListener);
} catch (Exception e) {
e.printStackTrace();
}
}
private void setAdapter() {
try {
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
} else {
mAdapter = new MessageAdapter(this, messagesList);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);
mLayoutManager.setSmoothScrollbarEnabled(true);
binding.recyclerViewLayout.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerViewLayout.recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
binding.recyclerViewLayout.recyclerView.setAdapter(mAdapter);
binding.recyclerViewLayout.recyclerView.setVisibility(View.VISIBLE);
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
请注意setAdapter()
方法,它在每个循环中调用,第一次它创建适配器,之后在循环的下一个增量中立即调用notifydatasetchanged
。
编辑:
完整代码: https://drive.google.com/file/d/11W8jL4_c9vVxVjZM9xBXo4KzH5LFViUn/view?usp=sharing
英文:
I thoroughly searched nd tried to implement diff diff things, but it seems not possible,
Requirement:
In my chat app, when I open particular user chat screen
- It should be scrolled to bottom so that user can see last msg/conversation
- When any new msg/new item will add at that time it should scroll to bottom
Please note, if I'll use below methods then my 1st requirement is not working
private void loadMessages() {
try {
DatabaseReference messageReference = rootReference.child(MESSAGES).child(currentUId).child(chatUser);
Query query = messageReference.orderByChild(TIME);
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
messagesList.clear();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
try {
Messages messages = ds.getValue(Messages.class);
messagesList.add(messages);
setAdapter();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
databaseError.getMessage();
}
};
query.addValueEventListener(valueEventListener);
} catch (Exception e) {
e.printStackTrace();
}
}
private void setAdapter() {
try {
if (mAdapter != null) {
mLayoutManager.smoothScrollToPosition(binding.recyclerViewLayout.recyclerView, null, messagesList.size());
mAdapter.notifyDataSetChanged();
} else {
mAdapter = new MessageAdapter(this, messagesList);
mLayoutManager.setStackFromEnd(false);
mLayoutManager.setSmoothScrollbarEnabled(true);
binding.recyclerViewLayout.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerViewLayout.recyclerView.setAdapter(mAdapter);
binding.recyclerViewLayout.recyclerView.setVisibility(View.VISIBLE);
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
And if I'll use below method, then my 2nd requirement is not working!
private void loadMessages() {
try {
DatabaseReference messageReference = rootReference.child(MESSAGES).child(currentUId).child(chatUser);
Query query = messageReference.orderByChild(TIME);
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
messagesList.clear();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
try {
Messages messages = ds.getValue(Messages.class);
messagesList.add(messages);
Comparator<Messages> compare = Collections.reverseOrder();
Collections.sort(messagesList, compare);
setAdapter();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
databaseError.getMessage();
}
};
query.addValueEventListener(valueEventListener);
} catch (Exception e) {
e.printStackTrace();
}
}
private void setAdapter() {
try {
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
} else {
mAdapter = new MessageAdapter(this, messagesList);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);
mLayoutManager.setSmoothScrollbarEnabled(true);
binding.recyclerViewLayout.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerViewLayout.recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
binding.recyclerViewLayout.recyclerView.setAdapter(mAdapter);
binding.recyclerViewLayout.recyclerView.setVisibility(View.VISIBLE);
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
Please pay attention on setAdapter()
as it is calling on for each loop, first time it is creating adapter and after that immediately with the next increment of loop, it calls notifydatasetchanged.
EDIT:
Complete code: https://drive.google.com/file/d/11W8jL4_c9vVxVjZM9xBXo4KzH5LFViUn/view?usp=sharing
答案1
得分: 1
Edit 4:
-
通过在 Adapter 中添加一个名为
addItem()
的函数来使其工作,然后在addItem()
函数中添加notifyItemInserted(messagesList.size());
-
在填充
List<Messages>
之后,添加recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
-
在每次
sendButtonClickEvent()
后添加recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
NOTE: 完整的代码在下面给出的链接中。
MainActivity
package studio.devcode.recyclerviewdemo;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import studio.devcode.recyclerviewdemo.MessageAdapter;
import studio.devcode.recyclerviewdemo.Messages;
import studio.devcode.recyclerviewdemo.R;
public class MainActivity extends AppCompatActivity {
// ...(略去其余部分,不再列出)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLayoutManager = new LinearLayoutManager(this);
recyclerView = findViewById(R.id.recycler_view_layout);
messagesList = new ArrayList<>();
// ...(略去其余部分,不再列出)
}
// ...(略去其余部分,不再列出)
private void onSendButtonClick(){
imageViewSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!editTextMessage.getText().toString().isEmpty() || !editTextMessage.getText().toString().equals(" ")){
mAdapter.addItem(new Messages(editTextMessage.getText().toString().toUpperCase(), "text", "Ali"));
editTextMessage.setText("");
recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
}
}
});
}
// ...(略去其余部分,不再列出)
private void onSoftInputKeyboardOpen(){
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v,
int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
recyclerView.smoothScrollToPosition(
recyclerView.getAdapter().getItemCount() - 1);
}
}, 100);
}
}
});
}
}
Messages
package studio.devcode.recyclerviewdemo;
public class Messages {
// ...(略去其余部分,不再列出)
public Messages(String message,String type,String from) {
this.message = message;
this.from = from;
this.type = type;
}
// ...(略去其余部分,不再列出)
}
MessageAdapter
package studio.devcode.recyclerviewdemo;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {
// ...(略去其余部分,不再列出)
@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {
try {
Messages messages = messagesList.get(position);
if (messages != null) {
String messageType = messages.getType();
if (checkNull(messages.getFrom()) && messages.getFrom().equals(currentUid)) {
if (checkNull(messageType) && messageType.equals("text")) {
holder.chatText_1.setText(messages.getMessage());
holder.messageSingleLayout.setVisibility(View.GONE);
holder.messageSingleLayout_1.setVisibility(View.VISIBLE);
}
} else {
if (checkNull(messageType) && messageType.equals("text")) {
holder.chatText.setText(messages.getMessage());
holder.messageSingleLayout.setVisibility(View.VISIBLE);
holder.messageSingleLayout_1.setVisibility(View.GONE);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int getItemCount() {
return messagesList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
// ...(略去其余部分,不再列出)
}
public void addItem(Messages message) {
messagesList.add(message);
notifyItemInserted(messagesList.size());
}
}
如果你需要查看完整的代码,请前往提供的 GitHub 仓库链接。
英文:
Edit 4:
-
Got it working (by adding a function
addItem()
in Adapter,notifyItemInserted(messagesList.size());
inaddItem()
function -
Added
recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
right after populating theList<Messages>
3.After every sendButtonClickEvent()
added recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
NOTE: The complete code is in the link given below.
MainActivity
package studio.devcode.recyclerviewdemo;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import studio.devcode.recyclerviewdemo.MessageAdapter;
import studio.devcode.recyclerviewdemo.Messages;
import studio.devcode.recyclerviewdemo.R;
public class MainActivity extends AppCompatActivity {
private ImageView imageViewSend;
private EditText editTextMessage;
private List<Messages> messagesList;
private MessageAdapter mAdapter;
private LinearLayoutManager mLayoutManager;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLayoutManager = new LinearLayoutManager(this);
recyclerView = findViewById(R.id.recycler_view_layout);
messagesList = new ArrayList<>();
imageViewSend = findViewById(R.id.send);
editTextMessage = findViewById(R.id.type_message);
setData("12345", "abc");
setData("12345", "hello");
setData("123145", "you");
setData("12345", "there");
setData("121345", "aqqqbc");
setData("123145", "abqqc");
setData("123145", "aqqbc");
setData("12345", "awwbc");
setData("123145", "eeabc");
setData("12345", "abeeec");
setData("112345", "addbc");
setData("12345", "abdcc");
setData("112345", "abccc");
setData("121345", "abccc");
setData("12345", "abccc");
setAdapter();
recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
onSendButtonClick();
onSoftInputKeyboardOpen();
}
private void setData(String from, String message) {
messagesList.add(new Messages(message, "text", from));
}
private void onSendButtonClick(){
imageViewSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!editTextMessage.getText().toString().isEmpty() || !editTextMessage.getText().toString().equals(" ")){
mAdapter.addItem(new Messages(editTextMessage.getText().toString().toUpperCase(), "text", "Ali"));
editTextMessage.setText("");
recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
}
}
});
}
private void setAdapter() {
try {
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
recyclerView.scrollToPosition(mAdapter.getItemCount());
} else {
mAdapter = new MessageAdapter(this, messagesList, "12345");
mLayoutManager.setSmoothScrollbarEnabled(true);
mLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(mAdapter);
recyclerView.setVisibility(View.VISIBLE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void onSoftInputKeyboardOpen(){
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v,
int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
recyclerView.smoothScrollToPosition(
recyclerView.getAdapter().getItemCount() - 1);
}
}, 100);
}
}
});
}
}
Messages
package studio.devcode.recyclerviewdemo;
public class Messages {
private String message;
private String type;
private String from;
private long time;
private boolean seen;
public Messages(String message,String type,String from) {
this.message = message;
this.from = from;
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
}
MessageAdapter
package studio.devcode.recyclerviewdemo;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {
private Context context;
private List<Messages> messagesList;
private String currentUid;
public MessageAdapter(Context context, List<Messages> messagesList, String currentUid) {
try {
this.currentUid = currentUid;
this.messagesList = messagesList;
this.context = context;
} catch (Exception e) {
e.printStackTrace();
}
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = null;
MyViewHolder myViewHolder;
try {
view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.message_single_layout, parent, false);
} catch (Exception e) {
e.printStackTrace();
}
myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
protected void showLog(String msg) {
Log.d(this.getClass().getSimpleName(), msg);
}
protected boolean checkNull(String data) {
return data != null && (!data.isEmpty());
}
@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {
try {
Messages messages = messagesList.get(position);
if (messages != null) {
String messageType = messages.getType();
if (checkNull(messages.getFrom()) && messages.getFrom().equals(currentUid)) {
if (checkNull(messageType) && messageType.equals("text")) {
holder.chatText_1.setText(messages.getMessage());
holder.messageSingleLayout.setVisibility(View.GONE);
holder.messageSingleLayout_1.setVisibility(View.VISIBLE);
}
} else {
if (checkNull(messageType) && messageType.equals("text")) {
holder.chatText.setText(messages.getMessage());
holder.messageSingleLayout.setVisibility(View.VISIBLE);
holder.messageSingleLayout_1.setVisibility(View.GONE);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int getItemCount() {
return messagesList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView chatText, chatText_1, chatTime, chatTime_1;
public LinearLayout messageSingleLayout, messageSingleLayout_1;
private final ImageView fileDownload;
private final ImageView fileDownload_1;
public MyViewHolder(View itemView) {
super(itemView);
chatText = itemView.findViewById(R.id.chatText);
chatText_1 = itemView.findViewById(R.id.chatText_1);
chatTime = itemView.findViewById(R.id.chatTime);
chatTime_1 = itemView.findViewById(R.id.chatTime_1);
messageSingleLayout = itemView.findViewById(R.id.messageSingleLayout);
messageSingleLayout_1 = itemView.findViewById(R.id.messageSingleLayout_1);
fileDownload = itemView.findViewById(R.id.fileDownload);
fileDownload_1 = itemView.findViewById(R.id.fileDownload_1);
}
}
public void addItem(Messages message) {
messagesList.add(message);
notifyItemInserted(messagesList.size());
}
}
Please give a star and also follow on GitHub
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论