RecyclerView обновляет представление при удалении объекта, за исключением последнего объекта

#java #android #firebase-realtime-database #arraylist #android-recyclerview

#java #Android #firebase-база данных в реальном времени #список массивов #android-recyclerview

Вопрос:

В моем приложении у меня есть действие, которое отображает ArrayList учащихся в RecyclerView. Учащиеся хранятся в базе данных Firebase в реальном времени. Пользователь может добавлять или удалять учащихся. Когда пользователь удаляет ученика, RecyclerView обновляется и показывает оставшихся учеников.

Но у меня проблема. Например, скажем, у меня было 5 студентов. Я хочу удалить все 5, поэтому я удаляю их один за другим. 4 из учащихся удаляют правильно, и RecyclerView обновляется и отражает это. Только когда я удаляю последнего ученика, RecyclerView не обновляется, и карточка ученика остается там. Я получаю всплывающее сообщение, подтверждающее удаление студента, и я вижу, что они были удалены в базе данных. Если я вернусь к панели мониторинга и вернусь к списку учащихся, CardView затем исчезнет. Странно.

Я пытался снова вызывать OnStart после каждого удаления, но это не помогло. Есть идеи?

Моя активность:

 public class StudentListActivity extends AppCompatActivity {

//firebase auth
private FirebaseAuth mAuth;

//public variables
public String currentUserAccount;
public String teacherAccountNav = "Teacher";
public String currentUserId;

//recyclerView variables
DatabaseReference ref;
ArrayList<Student> list;
RecyclerView recyclerView;
SearchView searchView;

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_student_list);

    //get current user
    mAuth = FirebaseAuth.getInstance();
    FirebaseUser user = mAuth.getCurrentUser();
    //assert current user is not null and get current users id
    assert user != null;
    currentUserId = user.getUid();

    //getting firebase reference of current users students
    ref = FirebaseDatabase.getInstance().getReference().child("students").child(currentUserId);

    //initialise views
    recyclerView = findViewById(R.id.rv);
    recyclerView.setHasFixedSize(true);
    searchView = findViewById(R.id.searchView);

}

@Override
protected void onStart() {
    super.onStart();
    if(ref != null){
        ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                //add students from firebase to an array list
                if(dataSnapshot.exists()){
                    list = new ArrayList<>();
                    for(DataSnapshot ds : dataSnapshot.getChildren()){
                        list.add(ds.getValue(Student.class));
                    }

                    //sort by name
                    Collections.sort(list, Student.myName);

                    //make recycler view
                    recyclerView.setLayoutManager(new LinearLayoutManager(StudentListActivity.this));
                    StudentAdapterClass studentAdapterClass = new StudentAdapterClass(list);
                    recyclerView.setAdapter(studentAdapterClass);

                    //click listeners for buttons
                    studentAdapterClass.setOnItemClickListener(new StudentAdapterClass.OnItemClickListener() {

                        @Override
                        public void onUpdateClick(int position) {
                            //handle update click in here
                            Student student = list.get(position);

                            //show update dialog here
                            showUpdateDialog(student.getStudentId(), student.getStudentName(), student.getStudentEmail());
                        }

                        @Override
                        public void onDeleteClick(int position){
                            //handle delete click in here
                            Student student = list.get(position);

                            //show delete dialog here
                            showDeleteDialog(student.getStudentId());
                        }

                    });

                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
                Toast.makeText(StudentListActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }//if

  
}


//dialog box for deleting student
private void showDeleteDialog(final String studentId){
    AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);

    LayoutInflater inflater = getLayoutInflater();

    //dialog XML
    final View dialogView = inflater.inflate(R.layout.delete_student_dialog, null);

    dialogBuilder.setView(dialogView);

    final ImageButton buttonNo = dialogView.findViewById(R.id.imageButtonNo);
    final ImageButton buttonYes = dialogView.findViewById(R.id.imageButtonYes);

    final AlertDialog alertDialog = dialogBuilder.create();
    alertDialog.show();

    buttonNo.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            alertDialog.dismiss();
        }
    });

    buttonYes.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            deleteStudent(studentId);

            alertDialog.dismiss();
        }
    });
}

//delete student method
private void deleteStudent(String id){
    //get current user
    mAuth = FirebaseAuth.getInstance();
    FirebaseUser user = mAuth.getCurrentUser();
    //assert current user is not null and get current users id
    assert user != null;
    currentUserId = user.getUid();

    DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("students").child(currentUserId).child(id);

    databaseReference.removeValue();

    Toast.makeText(this, "Student Deleted", Toast.LENGTH_LONG).show();

    //clear and get new list
    onStart();

}
  

Мой адаптер:

 public class StudentAdapterClass extends RecyclerView.Adapter<StudentAdapterClass.MyViewHolder> {

private ArrayList<Student> list;

private OnItemClickListener mListener;

public interface OnItemClickListener{
    //click method for update button
    void onUpdateClick(int position);
    //click method for delete button
    void onDeleteClick(int position);

}

public void setOnItemClickListener(OnItemClickListener listener){
    mListener = listener;
}

public StudentAdapterClass(ArrayList<Student> list){

    this.list = list;
}


@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
     View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_holder, viewGroup, false);
     return new MyViewHolder(view, mListener);
}

@Override
public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, int i) {
    myViewHolder.name.setText(list.get(i).getStudentName());
    myViewHolder.email.setText(list.get(i).getStudentEmail());

}

@Override
public int getItemCount() {

    return list.size();
}

static class MyViewHolder extends RecyclerView.ViewHolder {
    TextView name, email;
    ImageButton update, delete;
    MyViewHolder(@NonNull View itemView, final OnItemClickListener listener) {
        super(itemView);
        name = itemView.findViewById(R.id.studentName);
        email = itemView.findViewById(R.id.studentEmail);
        update = itemView.findViewById(R.id.updateButton);
        delete = itemView.findViewById(R.id.deleteButton);

        //update click listener
        update.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener != null){
                    int position = getAdapterPosition();
                    if(position != RecyclerView.NO_POSITION){
                        listener.onUpdateClick(position);
                    }
                }

            }
        });

        delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener != null){
                    int position = getAdapterPosition();
                    if(position != RecyclerView.NO_POSITION){
                        listener.onDeleteClick(position);
                    }
                }
            }
        });
    }
}
  

Комментарии:

1. Вызывается ли onDataChanged при последнем вызове OnStart()? Поместите logstatements в оба, чтобы узнать.

2. Вам лучше не вызывать OnStart() в конце deleteStudent().

3. Я не понимаю, что если у вас в базе данных пять учеников и вы начинаете свою деятельность, вызывается OnStart, и вы устанавливаете прослушиватель, для которого будет вызван onDataChange. И если нет, вы бы не устанавливали свой recycleview.

4. list = new ArrayList<>(); Вы всегда должны создавать новый список. Не только, если у вас есть снимок данных.

Ответ №1:

Каждый раз, когда изменяются данные RecyclerView, вы должны вызывать метод адаптера notifyDataSetChanged() , в противном случае RecyclerView не сможет обновиться корректно. Вы можете вызвать его внутри deleteStudent() метода следующим образом.

 recyclerView.getAdapter().notifyDataSetChanged()
  

Комментарии:

1. Спасибо за предложение, но оно не работает. По-прежнему происходит то же поведение.

2. Потому что вам также нужно установить для списка значение adapter. Я не знаю, как вы удалили элемент, вам нужно удалить элемент из list и снова обновить его до adapter.

3. Ах да! Я не удалял ученика из списка, а также из базы данных, это исправлено. Спасибо!

4. Нет, это не было проблемой, поскольку вы сказали, что четверо студентов удалили правильно. Чему я верю. Вы полагались не на notifyDataSetChanged, а на onDataChange и создали совершенно новый список и адаптер. Только для пятого ученика вы оставляете список неизменным, не создавая новый список.

5. И… если вы установите новый адаптер, что вы и делаете, вам вообще не нужно вызывать notifyDataSetChanged.