Search code examples
android-recyclerviewandroid-cardviewandroid-checkbox

CheckBox States Keep Changing While Scrolling in Recycler View


I am trying to make an Attendance App for my College. I am using CardView Inside a Recycler View. Inside my Card View is a checkBox to mark the attendance of the particular student. The problem is that if i mark say, Student 1,2 and 3. some random cards on position 10,11,12 or anything else(random) gets marked on it's own. i know it is because of the recycler view recycling the position of those cards but since i am new to development i unfortunately do not know how implement the Check Box to give me the details of the Card checked so that i can use it. I have been through a couple of guides but haven't been able to understand. I am attaching my Adapter Class and takeAttendance Activity below. i haven't implemented anything related to the check box yet and need guidance for the same.

RECYCLER VIEW ADAPTER CLASS

package com.pkg.attendanceapp;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.textview.MaterialTextView;

import java.util.ArrayList;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.StudentListViewHolder> {
    private ArrayList<StudentList> studentList;
    private ArrayList<String> presentStudents;
    private ArrayList<StudentList> absentStudents;

    public static class StudentListViewHolder extends RecyclerView.ViewHolder {

        private MaterialTextView studentName;
        private MaterialTextView studentRoll;
        private MaterialTextView attendanceTextview;
        private MaterialCheckBox checkBox;


        public StudentListViewHolder(@NonNull final View itemView) {
            super(itemView);

            studentName = itemView.findViewById(R.id.stdName);
            studentRoll = itemView.findViewById(R.id.stdRoll);
            checkBox = itemView.findViewById(R.id.checkBox);
            attendanceTextview = itemView.findViewById(R.id.studentAttendance);

        }
    }

    public RecyclerViewAdapter(ArrayList<StudentList> mStudentList){

        studentList = mStudentList;
        absentStudents = mStudentList;
    }

    @Override
    public StudentListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new StudentListViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.card_view, parent, false));

    }
    @Override
    public void onBindViewHolder(@NonNull final StudentListViewHolder holder, int position) {
        StudentList currentStudent = studentList.get(position);

        holder.studentName.setText(currentStudent.getStudName());
        holder.studentRoll.setText(currentStudent.getStudRoll());

    }
    @Override
    public int getItemCount() {
        return studentList == null ? 0 : studentList.size();

    }
}

ACTIVITY

package com.pkg.attendanceapp;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.DialogInterface;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.View;
import android.widget.RadioGroup;
import android.widget.Toast;

import com.google.android.gms.tasks.Task;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.textview.MaterialTextView;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;

public class takeAttendance extends AppCompatActivity {

    DatabaseReference AttendanceDatabaseReference;
    private RecyclerView recyclerView;
    private RecyclerViewAdapter recyclerViewAdapter;
    public ArrayList<StudentList> studentListArray;
    //private String date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_take_attendance);

        //FloatingActionButton saveBtn = findViewById(R.id.floatingActionButton);
        recyclerView = findViewById(R.id.mRecyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setHasFixedSize(true);
        recyclerViewAdapter = new RecyclerViewAdapter(studentListArray);
        recyclerView.setAdapter(recyclerViewAdapter);
        studentListArray = new ArrayList<>();



        AttendanceDatabaseReference = FirebaseDatabase.getInstance().getReference("studentsInfo");
        AttendanceDatabaseReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull final DataSnapshot dataSnapshot) {

                findViewById(R.id.progressBar).setVisibility(View.GONE);

                for (DataSnapshot mDataSnapshot : dataSnapshot.getChildren()){
                    StudentList studentsList = mDataSnapshot.getValue(StudentList.class);
                    studentListArray.add(studentsList);
                }

                recyclerViewAdapter = new RecyclerViewAdapter(studentListArray);
                recyclerView.setAdapter(recyclerViewAdapter);

            }
            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

                // Toast.makeText(takeAttendance.this, "Oops! Something Went Wrong...", Toast.LENGTH_LONG).show();
            }
        });
    }
 }


Solution

  • Based on other Posts/Guides, this is how it works -

    public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
    
    private ArrayList<StudentList> studentList;
    private ArrayList<String> mRollNum;
    private Activity activity;
    private OnCheckBoxClickListener mListener;
    
    private SparseBooleanArray itemStateArray = new SparseBooleanArray();
    
    public interface OnCheckBoxClickListener {
    
        void onCheckBoxClick(int position, boolean isChecked);
    }
    
    void setOnCheckBoxClickListener(OnCheckBoxClickListener listener){
    
        mListener = listener;
    }
    
     class ViewHolder extends RecyclerView.ViewHolder {
    
        private MaterialTextView studentName;
        private MaterialTextView studentRoll;
        private MaterialTextView attendanceStatus;
        MaterialCheckBox mCheckbox;
    
        ViewHolder(@NonNull View itemView, final OnCheckBoxClickListener listener) {
            super(itemView);
    
            studentName = itemView.findViewById(R.id.stdName);
            studentRoll = itemView.findViewById(R.id.stdRoll);
            attendanceStatus = itemView.findViewById(R.id.studentAttendance);
            mCheckbox = itemView.findViewById(R.id.checkBox);
    
            mCheckbox.setOnClickListener(new View.OnClickListener() {
                @SuppressLint("SetTextI18n")
                @Override
                public void onClick(View view) {
    
                    int adapterPosition = getAdapterPosition();
                    if (!itemStateArray.get(adapterPosition, false)) {
                        attendanceStatus.setTextColor(Color.GREEN);
                        attendanceStatus.setText("Present");
                        mCheckbox.setChecked(true);
                        itemStateArray.put(adapterPosition, true);
                        listener.onCheckBoxClick(adapterPosition, true);
    
                    }
                    else  {
                        mCheckbox.setChecked(false);
                        itemStateArray.put(adapterPosition, false);
                        listener.onCheckBoxClick(adapterPosition, false);
                        attendanceStatus.setTextColor(Color.RED);
                        attendanceStatus.setText("Absent");
    
    
                    }
    
    
                }
            });
        }
    
        @SuppressLint("SetTextI18n")
        void bind(int position) {
    
            if (!itemStateArray.get(position, false)) {
                mCheckbox.setChecked(false);
                attendanceStatus.setTextColor(Color.RED);
                attendanceStatus.setText("Absent");
    
            }
            else {
                attendanceStatus.setTextColor(Color.GREEN);
                attendanceStatus.setText("Present");
                mCheckbox.setChecked(true);
    
            }
        }
    }
    
    RecyclerViewAdapter(Activity activity, ArrayList<StudentList> mStudentList, ArrayList<String> rollNum ) {
        studentList = mStudentList;
        this.activity = activity;
        this.mRollNum = rollNum;
    
    }
    
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
        View studentListView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.card_view, parent, false);
    
        return new ViewHolder(studentListView, mListener);
    }
    
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    
        StudentList currentStudent = studentList.get(position);
    
        holder.studentName.setText(currentStudent.getStudName());
        holder.studentRoll.setText(currentStudent.getStudRoll());
    
        holder.bind(position);
    }
    
    @Override
    public int getItemCount() {
        return studentList == null ? 0 : studentList.size();
    }