Search code examples
javaandroidandroid-recyclerviewandroid-view

How can I stop my recyclerView view type from changing after scrolling?


Inside my RecyclerView Adapter class I have 2 view types to display the results of my query:

 @Query("SELECT l.log_id, l.junction_id ,l.date, l.workout_id, l.total_weight_lifted,
         l.reps, l.set_number FROM log_entries_table 
         AS l LEFT JOIN exercise_workout_junction_table AS ej 
         ON ej.exercise_workout_id = l.junction_id WHERE ej.exercise_id = :exerciseID 
         ORDER BY substr(l.date, -4) DESC, substr(l.date, -7) DESC, (l.date) DESC")

    LiveData<List<Log_Entries>> getAllExerciseHistoryLogs(int exerciseID);

The first view type is used to display all logEntries in which the date is unique: 1

The second view type is to display the rest of the logEntries which share the same date as the above:

2

My current code works fine, however every time I scroll down and the recyclerView updates, all the log-Entries with 'unique' dates (which should use the first viewType) get changed to display the second view type.

How can I stop my recyclerView view type from changing?

Before scroll -> After Scroll

RecyclerView Adapter


public class ExerciseHistoryAdapter2 extends RecyclerView.Adapter {

    private OnItemClickListener listener;
    private List<Log_Entries> allLogEntries = new ArrayList<>();
    private List<String> uniqueDates = new ArrayList<>();
    String logEntryDate;

    public void setExercises(List<Log_Entries> allLogEntries) {
        this.allLogEntries = allLogEntries;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view;

        if (viewType == 0) {
            view = layoutInflater.inflate(R.layout.exercise_history_item, parent, false);
            return new ViewHolderOne(view);
        }

        view = layoutInflater.inflate(R.layout.exercise_history_item_two, parent, false);
        return new ViewHolderTwo(view);
    }

    @Override
    public long getItemId(int position) {

        return allLogEntries.get(position).getLog_id();
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        logEntryDate = allLogEntries.get(position).getDate();

        if (uniqueDates.contains(logEntryDate)) {
            // bindViewHolder2
            ViewHolderTwo viewHolderTwo = (ViewHolderTwo) holder;
            viewHolderTwo.textViewWeight.setText(String.valueOf(allLogEntries.get(position).getTotal_weight_lifted()));
            viewHolderTwo.textViewReps.setText(String.valueOf(allLogEntries.get(position).getReps()));

        } else {
            uniqueDates.add(logEntryDate);
            //bind viewholder1
            ViewHolderOne viewHolderOne = (ViewHolderOne) holder;
            viewHolderOne.textViewDate.setText(allLogEntries.get(position).getDate());
            viewHolderOne.textViewWeight.setText(String.valueOf(allLogEntries.get(position).getTotal_weight_lifted()));
            viewHolderOne.textViewReps.setText(String.valueOf(allLogEntries.get(position).getReps()));
        }
    }

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

 @Override
    public int getItemViewType(int position) {

        logEntryDate = allLogEntries.get(position).getDate();

        if (uniqueDates.contains(logEntryDate)) {
            return 1;
        }
        return 0;
    }

    class ViewHolderOne extends RecyclerView.ViewHolder {
        private TextView textViewDate;
        private TextView textViewWeight;
        private TextView textViewReps;

        public ViewHolderOne(@NonNull View itemView) {
            super(itemView);

            textViewDate = itemView.findViewById(R.id.textView_dateH);
            textViewWeight = itemView.findViewById(R.id.textView_weightH);
            textViewReps = itemView.findViewById(R.id.textView_repss);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    if (listener != null && position != RecyclerView.NO_POSITION) {
                        listener.onItemClick(allLogEntries.get(position));
                    }
                }
            });
        }
    }

    class ViewHolderTwo extends RecyclerView.ViewHolder {
        private TextView textViewWeight;
        private TextView textViewReps;

        public ViewHolderTwo(@NonNull View itemView) {
            super(itemView);
            textViewWeight = itemView.findViewById(R.id.textView_weightH2);
            textViewReps = itemView.findViewById(R.id.textView_repss2);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    if (listener != null && position != RecyclerView.NO_POSITION) {
                        listener.onItemClick(allLogEntries.get(position));
                    }
                }
            });

        }
    }

    public interface OnItemClickListener {
        void onItemClick(Log_Entries log_entries);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }
}

Solution

  • Your getItemViewType and onBindViewHolder has some issues.

    @Override
    public int getItemViewType(int position) {
        // type 0 = with date header
        // type 1 = without date header
    
        // if list is sorted chronologically
        if (position == 0) {
            return 0
        }
    
        String currentDate = allLogEntries.get(position).getDate();
        String previousDate = allLogEntries.get(position - 1).getDate();
    
        if (currentDate.equals(previousDate)) {
            return 1
        } else {
            return 0
        }
    }
    

    The first item will always have the header since the list is sorted chronologically. For the rest of the items, you need to check whether the date for the current item is the same as the previous item. Based on that condition you return the type.

    You do not need to manage a list of unique dates. You have shared mutable states between the functions that are being called multiple times and are not synced. Just delete these and the references of them from onBindViewHolder

    private List<String> uniqueDates = new ArrayList<>();
    String logEntryDate;