当列表填满屏幕时,RecyclerView 显示第一项的副本

Posted

技术标签:

【中文标题】当列表填满屏幕时,RecyclerView 显示第一项的副本【英文标题】:RecyclerView displays a duplicate of the first item when the list fills the screen 【发布时间】:2019-02-21 15:10:48 【问题描述】:

我在 ListItemView / RecyclerView 中有几个项目。添加项目时,它会正确更新视图,并且项目按添加顺序显示。

但是,当我浏览多个项目以使所有视图都填满屏幕(可以滚动)时,它会再次将第一个项目显示为副本,而不是显示最新添加的项目。

我的 GetView 方法:

@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) 
    View listItemView = convertView;

    Log.d(TAG, "The GetView method was called");

    ChatConversationMessage currentMessage = getItem(position);

    if (listItemView == null) 
        if (currentMessage.getSentTextMessage() != null) 

            Log.d(TAG, "Sent message detected");
            // Sent text
            listItemView = LayoutInflater.from(getContext()).inflate(
                    R.layout.chat_conversation_txt_sent, parent, false);
            TextView sentText = listItemView.findViewById(R.id.textview_chat_message_sent);
            sentText.setText(currentMessage.getSentTextMessage());
        

        if (currentMessage.getReceivedTextMessage() != null) 

            Log.d(TAG, "Received message detected");
            // Received text
            listItemView = LayoutInflater.from(getContext()).inflate(
                    R.layout.chat_conversation_txt_received, parent, false);
            TextView receivedText = listItemView.findViewById(R.id.textview_chat_message_received);
            receivedText.setText(currentMessage.getReceivedTextMessage());
            ImageView chatHead = listItemView.findViewById(R.id.imageview_chat_head_received);
            chatHead.setImageResource(currentMessage.getImageResourceId());
            chatHead.setClipToOutline(true);
        
    

    return listItemView;

还有我的 getLiveMessages 方法(来自 Firestore)

public void getLiveChatMessages(final ArrayList<ChatConversationMessage> messageArrayList, final ChatConversationMessageAdapter adapter) 

            messagesCollectionRef.addSnapshotListener(new EventListener<QuerySnapshot>() 
                @Override
                public void onEvent(@Nullable QuerySnapshot snapshots,
                                    @Nullable FirebaseFirestoreException e) 
                    if (e != null) 
                        Log.w(TAG, "listen:error", e);
                        return;
                    

                    for (DocumentChange dc : snapshots.getDocumentChanges()) 

                        switch (dc.getType()) 
                            case ADDED:
                                Log.d(TAG, "New message added" + dc.getDocument().getData());

                                if (dc.getDocument().get("Message") != null && dc.getDocument().get("From user with ID").equals(userID)) 
                                    String message = dc.getDocument().getString("Message");
                                    messageArrayList.add(new ChatConversationMessage(message));
                                    adapter.notifyDataSetChanged(); //Ensures messages are visible immediately

                                 else if (dc.getDocument().get("Message") != null) 
                                    String message = dc.getDocument().getString("Message");
                                    Log.e("TAG", "The received message is written to the array. Message: " + message);
                                    messageArrayList.add(new ChatConversationMessage(message, R.drawable.redhead_1));

                                    adapter.notifyDataSetChanged(); //Ensures messages are visible immediately

                                
                                Log.d(TAG, "Message ArrayList: " +  messageArrayList);
                                break;

                            case MODIFIED:
                                Log.d(TAG, "Message modified" + dc.getDocument().getData());
                                break;
                            case REMOVED:
                                Log.d(TAG, "Message removed" + dc.getDocument().getData());
                                break;
                        
                    

                
            );

【问题讨论】:

【参考方案1】:

您没有正确处理视图“回收”。 getView() 必须始终返回一个视图并设置子视图值,后者是您遇到麻烦的地方。

这是带注释的代码,显示了一些解决您的问题的建议:

public View getView(int position, View convertView, ViewGroup parent) 
    View listItemView = convertView;

    Log.d(TAG, "The GetView method was called");

    ChatConversationMessage currentMessage = getItem(position);

    // determine if Sent Message
    if (currentMessage.getSentTextMessage() != null) 
    

        Log.d(TAG, "Sent message detected");

        if (listItemView == null) 
            // inflate the layout as needed
            listItemView = LayoutInflater.from(getContext()).inflate(
                    R.layout.chat_conversation_txt_sent, parent, false);
        

        // now set the subview values
        TextView sentText = listItemView.findViewById(R.id.textview_chat_message_sent);
        sentText.setText(currentMessage.getSentTextMessage());
    

    // else a Received Message
    else if (currentMessage.getReceivedTextMessage() != null) 

        Log.d(TAG, "Received message detected");

        if (listItemView == null) 
            // inflate the layout as needed
            listItemView = LayoutInflater.from(getContext()).inflate(
                    R.layout.chat_conversation_txt_received, parent, false);
        

        // then set the subview values
        TextView receivedText =  listItemView.findViewById(R.id.textview_chat_message_received);
        receivedText.setText(currentMessage.getReceivedTextMessage());
        ImageView chatHead = listItemView.findViewById(R.id.imageview_chat_head_received);
        chatHead.setImageResource(currentMessage.getImageResourceId());
        chatHead.setClipToOutline(true);
    

    return listItemView;

您应该为您的两个布局考虑一个 ViewHolder,这将简化您的代码并使其在运行时更高效。不确定这是否是最好的链接,但应该会给你一些额外的想法:

About RecyclerView.ViewHolder and RecyclerView.Adapter

【讨论】:

我试过你的方法,但是应用程序在“setText”方法上崩溃了,说它不能在空对象引用上调用它。所以我尝试在每个 if 语句块中移动 setText 方法(和其余代码)。但这也不起作用,因为它弄乱了我的物品顺序:-( 崩溃可能是因为您没有正确使用 RecyclerView ...您有两个不同的视图(已发送和已接收)。见***.com/questions/26245139/… ,,,更具体地说,如果convertView是一个chat_conversation_txt_sent并且currentMessage是一个接收到的消息,那么listItemView将引用错误的viewGroup 谢谢。我根据 SendBird 的聊天应用教程从头开始实现了整个过程,发现很多东西都丢失了。现在可以使用了!

以上是关于当列表填满屏幕时,RecyclerView 显示第一项的副本的主要内容,如果未能解决你的问题,请参考以下文章

RecyclerView未正确显示消息列表

即使指定了“match_parent”,使用 ConstraintLayout 的 RecyclerView 项目也不会填满屏幕的整个宽度

在RecyclerView列表滚动的时候显示或者隐藏Toolbar

Android:在recyclerview下方显示按钮,但始终在屏幕上

RecyclerView 不显示从本地主机发送的 JSON 只是空白屏幕

RecyclerView初探