#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.