Reputation: 6971
Since the ValueEventListener
gets triggered every time the data in the Firebase Database
updates, it is also called when a new upload is done while the RecyclerView
is showing. The way I have it now causes duplicate entries when this happens, because the Upload
objects are already in the mUploads List
. And when an upload is done, the whole database gets queried again and all items added to the already existing ArrayList
I don't know how to solve this. Should I create a new Arraylist
every time the ValueEventListener is triggered or do I need a completely different callback?
Also the ValueEventListener
interfers with my mAdapter.notifyItemRemoved
call because the ValueEventListener
gets triggered as soon as I delete something.
public class ImagesActivity extends AppCompatActivity implements ImageAdapter.OnItemClickListener {
private RecyclerView mRecyclerView;
private ImageAdapter mAdapter;
private ProgressBar mProgressCircle;
private DatabaseReference mDatabaseRef;
private FirebaseStorage mStorage;
private List<Upload> mUploads;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_images);
mUploads = new ArrayList<>();
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mProgressCircle = findViewById(R.id.progress_circle);
mDatabaseRef = FirebaseDatabase.getInstance().getReference("uploads");
mStorage = FirebaseStorage.getInstance();
mDatabaseRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Upload upload = postSnapshot.getValue(Upload.class);
upload.setKey(postSnapshot.getKey());
mUploads.add(upload);
}
mAdapter = new ImageAdapter(ImagesActivity.this, mUploads);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(ImagesActivity.this);
mProgressCircle.setVisibility(View.INVISIBLE);
}
@Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(ImagesActivity.this, databaseError.getMessage(), Toast.LENGTH_LONG).show();
mProgressCircle.setVisibility(View.INVISIBLE);
}
});
}
@Override
public void onDeleteClick(final int position) {
Upload selectedItem = mUploads.get(position);
final String selectedKey = selectedItem.getKey();
StorageReference imageRef = mStorage.getReferenceFromUrl(selectedItem.getImageUrl());
imageRef.delete().addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
mDatabaseRef.child(selectedKey).removeValue();
mUploads.remove(position);
mAdapter.notifyItemRemoved(position);
}
});
}
}
Upvotes: 3
Views: 5780
Reputation: 599571
Instead of using a ValueEventListener
consider attaching a ChildEventListener
. This type of listener gets triggered for the individual child nodes that are added, changed, or removed.
When you initially attach a ChildEventListener
its onChildAdded
will be called for each child that the listener matches. Then when you add a child node to the database later, the onChildAdded
will be called again with only the existing child. This makes it super easy to update the UI, just add the new child to the adapter.
A ChildEventListener
also has an onChildChanged
event, which gets triggered when a specific child is changed. Here too, the method gets triggered with only the existing child, once again making it easy to update just that child in the adapter.
See listen for child events in the Firebase documentation for more on this type of listener.
Upvotes: 3
Reputation: 3100
clear mUploads
before adding data in addValueEventListener
like below
mDatabaseRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
mUploads.clear(); //change here
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Upload upload = postSnapshot.getValue(Upload.class);
upload.setKey(postSnapshot.getKey());
mUploads.add(upload);
}
mAdapter = new ImageAdapter(ImagesActivity.this, mUploads);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(ImagesActivity.this);
mProgressCircle.setVisibility(View.INVISIBLE);
}
Upvotes: 5
Reputation: 18610
I suggest that you use addListenerForSingleValueEvent
so that the data will be fetched once and at the end add addChildEventListener
and override onChildAdded
so new add items will be fetched also , I think this better than clearing the list and re-inflating the recyclerview
checkout the code
mDatabaseRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Upload upload = postSnapshot.getValue(Upload.class);
upload.setKey(postSnapshot.getKey());
mUploads.add(upload);
}
mAdapter = new ImageAdapter(ImagesActivity.this, mUploads);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(ImagesActivity.this);
mProgressCircle.setVisibility(View.INVISIBLE);
mDatabaseRef.addChildEventListener(new ChildEventListener(){
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Upload upload = postSnapshot.getValue(Upload.class);
upload.setKey(postSnapshot.getKey());
mUploads.add(upload);
}
})
}
@Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(ImagesActivity.this, databaseError.getMessage(), Toast.LENGTH_LONG).show();
mProgressCircle.setVisibility(View.INVISIBLE);
}
});
Upvotes: 1
Reputation: 5597
You can use addListenerForSingleValueEvent()
and this will read the data once, if you want to refresh the data, you can call it again, for more info
Firebase - read data once
Upvotes: 1
Reputation: 4220
Try this
clear arraylist mUploads.clear()
before addValueEventListener like below
mUploads.clear();
Upvotes: 2