为什么 RecyclerView 的 ScrollToBottom 功能效率不高?

huangapple go评论81阅读模式
英文:

Why does recyclerview ScrollToBottom is not working efficiently?

问题

我彻底搜索并尝试实现了不同的事情,但似乎不可能,

要求:
在我的聊天应用中,当我打开特定用户的聊天界面时,

  1. 它应该滚动到底部,以便用户可以看到最后一条消息/对话
  2. 当有任何新消息/新项目添加时,它应该滚动到底部

请注意,如果我使用以下方法,则我的第一个要求无法实现

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

  1. It should be scrolled to bottom so that user can see last msg/conversation
  2. 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&lt;Messages&gt; 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:

  1. 通过在 Adapter 中添加一个名为 addItem() 的函数来使其工作,然后在 addItem() 函数中添加 notifyItemInserted(messagesList.size());

  2. 在填充 List&lt;Messages&gt; 之后,添加 recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);

  3. 在每次 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&lt;&gt;();
        // ...(略去其余部分,不再列出)
    }

    // ...(略去其余部分,不再列出)

    private void onSendButtonClick(){
        imageViewSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(!editTextMessage.getText().toString().isEmpty() || !editTextMessage.getText().toString().equals(&quot; &quot;)){
                    mAdapter.addItem(new Messages(editTextMessage.getText().toString().toUpperCase(), &quot;text&quot;, &quot;Ali&quot;));
                    editTextMessage.setText(&quot;&quot;);
                    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 &lt; 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&lt;MessageAdapter.MyViewHolder&gt; {
    // ...(略去其余部分,不再列出)

    @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()) &amp;&amp; messages.getFrom().equals(currentUid)) {
                    if (checkNull(messageType) &amp;&amp; messageType.equals(&quot;text&quot;)) {
                        holder.chatText_1.setText(messages.getMessage());
                        holder.messageSingleLayout.setVisibility(View.GONE);
                        holder.messageSingleLayout_1.setVisibility(View.VISIBLE);
                    }
                } else {
                    if (checkNull(messageType) &amp;&amp; messageType.equals(&quot;text&quot;)) {
                        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:

  1. Got it working (by adding a function addItem() in Adapter, notifyItemInserted(messagesList.size()); in addItem() function

  2. Added recyclerView.scrollToPosition(mAdapter.getItemCount() - 1); right after populating the List&lt;Messages&gt;

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&lt;Messages&gt; 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&lt;&gt;();
imageViewSend = findViewById(R.id.send);
editTextMessage = findViewById(R.id.type_message);
setData(&quot;12345&quot;, &quot;abc&quot;);
setData(&quot;12345&quot;, &quot;hello&quot;);
setData(&quot;123145&quot;, &quot;you&quot;);
setData(&quot;12345&quot;, &quot;there&quot;);
setData(&quot;121345&quot;, &quot;aqqqbc&quot;);
setData(&quot;123145&quot;, &quot;abqqc&quot;);
setData(&quot;123145&quot;, &quot;aqqbc&quot;);
setData(&quot;12345&quot;, &quot;awwbc&quot;);
setData(&quot;123145&quot;, &quot;eeabc&quot;);
setData(&quot;12345&quot;, &quot;abeeec&quot;);
setData(&quot;112345&quot;, &quot;addbc&quot;);
setData(&quot;12345&quot;, &quot;abdcc&quot;);
setData(&quot;112345&quot;, &quot;abccc&quot;);
setData(&quot;121345&quot;, &quot;abccc&quot;);
setData(&quot;12345&quot;, &quot;abccc&quot;);
setAdapter();
recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
onSendButtonClick();
onSoftInputKeyboardOpen();
}
private void setData(String from, String message) {
messagesList.add(new Messages(message, &quot;text&quot;, from));
}
private void onSendButtonClick(){
imageViewSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!editTextMessage.getText().toString().isEmpty() || !editTextMessage.getText().toString().equals(&quot; &quot;)){
mAdapter.addItem(new Messages(editTextMessage.getText().toString().toUpperCase(), &quot;text&quot;, &quot;Ali&quot;));
editTextMessage.setText(&quot;&quot;);
recyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
}
}
});
}
private void setAdapter() {
try {
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
recyclerView.scrollToPosition(mAdapter.getItemCount());
} else {
mAdapter = new MessageAdapter(this, messagesList, &quot;12345&quot;);
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 &lt; 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&lt;MessageAdapter.MyViewHolder&gt; {
private Context context;
private List&lt;Messages&gt; messagesList;
private String currentUid;
public MessageAdapter(Context context, List&lt;Messages&gt; 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 &amp;&amp; (!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()) &amp;&amp; messages.getFrom().equals(currentUid)) {
if (checkNull(messageType) &amp;&amp; messageType.equals(&quot;text&quot;)) {
holder.chatText_1.setText(messages.getMessage());
holder.messageSingleLayout.setVisibility(View.GONE);
holder.messageSingleLayout_1.setVisibility(View.VISIBLE);
}
} else {
if (checkNull(messageType) &amp;&amp; messageType.equals(&quot;text&quot;)) {
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 为什么 RecyclerView 的 ScrollToBottom 功能效率不高?

Github Repo to complete Demo

Vido URL

huangapple
  • 本文由 发表于 2020年8月22日 02:01:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/63527901.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定