Search code examples
androidandroid-recyclerviewnestedrecyclerview

Losing data while scrolling in Nested RecyclerView


I am working on a chatbot in which there are two view types in the parent adapter, in which the ReceivedMessageViewHolder has nested recycler view, I am currently removing the visibility of the nested recycler view on click of any item inside the nested recycler view but and it seems to be working fine. But when I scroll from bottom-top-bottom, I end up losing data from the last item of nested recycler view, the parent item is still showing but the child adapter is gone while scrolling.

I am adding my code for ParentAdapter, ChildAdapter, and ItemClick Listener. Please have a look and help me figure out the mistake I am doing or how to encounter the problem.

Code for ParentAdapter:

public class MessageListAdapter extends RecyclerView.Adapter {
    private Context mContext;
    private static final int VIEW_TYPE_MESSAGE_SENT = 1;
    private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;
    private List<Payload> chatMessageList;
    private OnItemClickListener onItemClickListener;

    MessageListAdapter(Context context, List<Payload> chatMessageList, OnItemClickListener onItemClickListener) {
        this.mContext = context;
        this.chatMessageList = chatMessageList;
        this.onItemClickListener = onItemClickListener;
    }

    @Override
    public int getItemCount() {
        return chatMessageList.size();
    }

    // Determines the appropriate ViewType according to the sender of the message.
    @Override
    public int getItemViewType(int position) {
        Payload message = chatMessageList.get(position);
        if (TextUtils.equals(message.getUserType(), "USER")) {
            // If the current user is the sender of the message
            return VIEW_TYPE_MESSAGE_SENT;
        } else {
            // If some other user sent the message
            return VIEW_TYPE_MESSAGE_RECEIVED;
        }
    }


    // Inflates the appropriate layout according to the ViewType.
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        if (viewType == VIEW_TYPE_MESSAGE_SENT) {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_user_msg, parent, false);
            return new SentMessageHolder(view);
        } else {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_bot_msg, parent, false);
            return new ReceivedMessageHolder(view);
        }
    }


    // Passes the message object to a ViewHolder so that the contents can be bound to UI.
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Payload chatMessage = chatMessageList.get(position);
        switch (holder.getItemViewType()) {
            case VIEW_TYPE_MESSAGE_SENT:
                ((SentMessageHolder) holder).bind(chatMessage.getMessage(), getFormattedTime());
                break;
            case VIEW_TYPE_MESSAGE_RECEIVED:
                ((ReceivedMessageHolder) holder).bind(chatMessage.getMessage(), getFormattedTime());
                ((ReceivedMessageHolder) holder).bindRv(chatMessage, mContext);
                break;
        }
    }

    private static class SentMessageHolder extends RecyclerView.ViewHolder {
        TextView txtViewChatMsgUser;
        TextView txtViewDateTimeUser;

        SentMessageHolder(View itemView) {
            super(itemView);
            txtViewChatMsgUser = itemView.findViewById(R.id.txtViewChatMsgUser);
            txtViewDateTimeUser = itemView.findViewById(R.id.txtViewDateTimeUser);
        }

        void bind(String message, String time) {
            txtViewChatMsgUser.setText(message);
            txtViewDateTimeUser.setText(time);
        }
    }

    private class ReceivedMessageHolder extends RecyclerView.ViewHolder {
        TextView txtViewChatMsgBot;
        RecyclerView rvDeepLink;
        TextView txtViewDateTimeBot;

        ReceivedMessageHolder(View itemView) {
            super(itemView);
            txtViewChatMsgBot = itemView.findViewById(R.id.txtViewChatMsgBot);
            rvDeepLink = itemView.findViewById(R.id.itemRvDeepLink);
            txtViewDateTimeBot = itemView.findViewById(R.id.txtViewDateTimeBot);
        }

        void bind(String message, String time) {
            txtViewChatMsgBot.setText(message);
            txtViewDateTimeBot.setText(time);
        }

        void bindRv(Payload chatMessage, Context context) {
            if (chatMessage.getData() != null && !chatMessage.isOptionSelected()) {
                LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
                linearLayoutManager.setOrientation(RecyclerView.VERTICAL);
                rvDeepLink.setLayoutManager(linearLayoutManager);
                rvDeepLink.setHasFixedSize(true);
                DeepLinkAdapter deepLinkAdapter = new DeepLinkAdapter(context, chatMessage.getData(), onItemClickListener, getAdapterPosition());
                rvDeepLink.setAdapter(deepLinkAdapter);
            } else {
                rvDeepLink.setVisibility(View.GONE);
            }
        }
    }

    private String getFormattedTime() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("hh.mm aa", Locale.ENGLISH);
        return dateFormat.format(new Date());
    }

    void onSelected(int position) {
        chatMessageList.get(position).setOptionSelected(true);
        notifyItemChanged(position);
    }

}

Code for Child Adapter:

public class DeepLinkAdapter extends RecyclerView.Adapter<DeepLinkAdapter.DeepLinkViewHolder> {

    private List<DataItem> dataItemList;
    private Context context;
    private OnItemClickListener onItemClickListener;
    private int parentPos;

    DeepLinkAdapter(Context context, List<DataItem> dataItemList, OnItemClickListener onItemClickListener, int parentPos) {
        this.context = context;
        this.dataItemList = dataItemList;
        this.onItemClickListener = onItemClickListener;
        this.parentPos = parentPos;
    }

    @NonNull
    @Override
    public DeepLinkViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new DeepLinkViewHolder(LayoutInflater.from(context).inflate(R.layout.inner_bot_msg, parent, false), parentPos);
    }

    @Override
    public void onBindViewHolder(@NonNull final DeepLinkViewHolder holder, final int position) {
        DataItem dataItem = dataItemList.get(position);
        if (TextUtils.isEmpty(dataItem.getDeeplink())) {
            holder.deepLinkText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
        } else {
            holder.deepLinkText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_right_arrow, 0);
        }
        if (!TextUtils.isEmpty(dataItem.getText())) {
            holder.deepLinkText.setVisibility(View.VISIBLE);
            holder.deepLinkText.setText(dataItem.getText());
        } else {
            holder.deepLinkText.setVisibility(View.GONE);
        }
    }

    @Override
    public int getItemCount() {
        if (dataItemList != null) {
            return dataItemList.size();
        } else {
            return 0;
        }
    }


    class DeepLinkViewHolder extends RecyclerView.ViewHolder {
        private TextView deepLinkText;

        DeepLinkViewHolder(View itemView, int parentPos) {
            super(itemView);
            deepLinkText = itemView.findViewById(R.id.innerDeepLinkMsgText);
            if (onItemClickListener != null) {
                deepLinkText.setOnClickListener(v -> onItemClickListener.onItemClick(getAdapterPosition(), parentPos));
            }
        }
    }
}

Code for ItemClickListener on Activity:

@Override
    public void onItemClick(int childPos, int parentPos) {
        if (TextUtils.isEmpty(chatMessageList.get(parentPos).getData().get(childPos).getDeeplink())) {
            String message = chatMessageList.get(parentPos).getData().get(childPos).getText();
            if (!viewModel.sendMessage(message)) {
                setMessage(getString(R.string.error_something_went_wrong), ChatBotManager.UserType.BOT.toString());
            } else {
                messageListAdapter.onSelected(parentPos);
                setMessage(message, ChatBotManager.UserType.USER.toString());
            }
        } else {
            if (TextUtils.equals(chatMessageList.get(parentPos).getData().get(childPos).getDeeplink(), "restart")) {
                if (messageListAdapter != null) {
                    messageListAdapter.clearData();
                    viewModel.sendMessage("start");
                }
            } else {
                String uri = chatMessageList.get(parentPos).getData().get(childPos).getDeeplink();
                Intent newIntent = new Intent(ApplicationClass.getContext(), TalkLinkingActivity.class);
                newIntent.setData(Uri.parse(uri));
                newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(newIntent);
                finish();
            }
        }
    }

Help me find my mistake, Please help!


Solution

  • You setVisibility for rvDeepLink but never show it again, when view holder is recycled it still gone

          void bindRv(Payload chatMessage, Context context) {
                if (chatMessage.getData() != null && !chatMessage.isOptionSelected()) {
                    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
                    linearLayoutManager.setOrientation(RecyclerView.VERTICAL);
                    rvDeepLink.setLayoutManager(linearLayoutManager);
                    rvDeepLink.setHasFixedSize(true);
                    DeepLinkAdapter deepLinkAdapter = new DeepLinkAdapter(context, chatMessage.getData(), onItemClickListener, getAdapterPosition());
                    rvDeepLink.setAdapter(deepLinkAdapter);
                    rvDeepLink.setVisibility(View.VISIBLE);
                } else {
                    rvDeepLink.setVisibility(View.GONE);
                }
            }