I have an Activity with a RecyclerView that gets a list of objects from my Fireabase Database. Until this point everything works as expected. On the adapter I have a button do delete one object. This button deletes the object from both the list and from the database.
When click on it, the object is indeed deleted from both places. The problem that it duplicates the list on the RecyclerView.
How can I fix this behavior?
public class ApiariosListActivity extends AppCompatActivity implements ApiarioOnListAdapter.ApiarioClickListener {
private ApiarioOnListAdapter apiarioOnListAdapter;
private ArrayList<Apiario> apiList;
private static final int EDIT_APIARIO_REQUEST_CODE = 1;
private static final int ADD_APIARIO_REQUEST_CODE = 2;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apiarios_list);
RecyclerView recyclerView = findViewById(R.id.recyclerViewApiarios);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
this.apiList = new ArrayList<>();
DatabaseReference mDatabaseQuery = FirebaseDatabase.getInstance().getReference("apiario");
Query query = mDatabaseQuery;
query.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
for(DataSnapshot dataSnapshot : snapshot.getChildren()){
Apiario apiario = dataSnapshot.getValue(Apiario.class);
apiList.add(apiario);
}
apiarioOnListAdapter = new ApiarioOnListAdapter(apiList, ApiariosListActivity.this);
recyclerView.setAdapter(apiarioOnListAdapter);
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
// Add Apiario button
ImageButton btnAddApiario = findViewById(R.id.btnAddApiario);
btnAddApiario.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(ApiariosListActivity.this, SelectDistrictActivity.class);
startActivityForResult(intent, ADD_APIARIO_REQUEST_CODE);
}
});
}
@Override
protected void onRestart() {
super.onRestart();
Log.d("AnualDeclarationActivity", "JV: onRestart");
}
@Override
protected void onStart() {
super.onStart();
Log.d("AnualDeclarationActivity", "JV: onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d("AnualDeclarationActivity", "JV: onResume");
}
@Override
public void onEditClick(Apiario apiario) {
Log.d("AnualDeclarationActivity", "JV: onEditClick");
Intent intent = new Intent(this, EditApiarioActivity.class);
intent.putExtra("apiario", apiario);
startActivityForResult(intent, EDIT_APIARIO_REQUEST_CODE);
}
@Override
public void onDeleteClick(Apiario apiario) {
// Implement delete functionality, e.g., show a confirmation dialog
showDeleteConfirmationDialog(apiario);
}
private void showDeleteConfirmationDialog(final Apiario apiario) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Delete Apiario");
builder.setMessage("Are you sure you want to delete this Apiario?");
builder.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// Perform the delete operation
deleteApiario(apiario);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// Cancel the delete operation
dialogInterface.dismiss();
}
});
builder.create().show();
}
private void deleteApiario(Apiario apiario) {
// Remove the apiario from the list
apiList.remove(apiario);
// Notify the adapter of the change
apiarioOnListAdapter.notifyDataSetChanged();
// Inform the user
Toast.makeText(this, "Apiario deleted: " + apiario.getNomeApiario(), Toast.LENGTH_SHORT).show();
}
private class PostDataTask extends AsyncTask<List<Apiario>, Void, String> {
@Override
protected void onPreExecute() {
Log.d("AsyncTask", "Executing onPreExecute");
}
@Override
protected String doInBackground(List<Apiario>... apiariosList) {
Log.d("AsyncTask", "Execute doInBackground");
// Create a JSON array to hold multiple Apiario objects
JSONArray apiarioArray = new JSONArray();
// Convert each Apiario object to a JSON object and add it to the array
for (Apiario apiario : apiariosList[0]) {
try {
JSONObject apiarioObject = new JSONObject();
apiarioObject.put("nomeApiario", apiario.getNomeApiario());
apiarioObject.put("apiarioDeTransumancia", apiario.isApiarioDeTransumancia()); // Example value, modify as needed
apiarioObject.put("numColmeias", apiario.getNumColmeias());
apiarioObject.put("numNucleos", apiario.getNumNucleos());
apiarioObject.put("coordenadaX", apiario.getCoordenadaX());
apiarioObject.put("coordenadaY", apiario.getCoordenadaY());
apiarioObject.put("freguesia", apiario.getFreguesia());
apiarioArray.put(apiarioObject);
} catch (JSONException e) {
e.printStackTrace();
}
}
// Create the main JSON object with the array
JSONObject mainJsonObject = new JSONObject();
try {
mainJsonObject.put("declarationModel", apiarioArray);
} catch (JSONException e) {
e.printStackTrace();
}
ApiCaller apiCaller = new ApiCaller();
String apiUrl = "http://10.0.2.2:5176/api/DeclarationController/SubmitAnualDeclaration";
String jsonInputString = mainJsonObject.toString();
Log.d("AsyncTask", "doInBackground: Making install request with JSON: " + jsonInputString);
String result = apiCaller.makePost(apiUrl, jsonInputString);
if (result != null) {
Log.d("AsyncTask", "doInBackground: Submission successful. Response: " + result);
return result;
} else {
Log.d("AsyncTask", "doInBackground: Submission request failed.");
return null;
}
}
@Override
protected void onPostExecute(String result) {
Log.d("AsyncTask", "FP : Executing onPostExecute");
if (result != null) {
Toast.makeText(ApiariosListActivity.this, "Success", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(ApiariosListActivity.this, "Error", Toast.LENGTH_LONG).show();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d("TEST", "JV: onActivityResult - requestCode: " + requestCode + ", resultCode: " + resultCode);
Log.d("TEST", "JV: ON ACTIVITY");
if (requestCode == ADD_APIARIO_REQUEST_CODE && resultCode == RESULT_OK && data != null) {
// Retrieve the newly added Apiario object
Apiario newApiario = data.getParcelableExtra("newApiario");
// Add the new Apiario to the list
apiList.add(newApiario);
// Notify the adapter of the change
apiarioOnListAdapter.notifyDataSetChanged();
}
if (requestCode == EDIT_APIARIO_REQUEST_CODE && resultCode == RESULT_OK && data != null) {
Apiario modifiedApiario = data.getParcelableExtra("modifiedApiario");
int position = findPositionOfApiarioInList(modifiedApiario);
if (position != -1) {
apiList.set(position, modifiedApiario);
apiarioOnListAdapter.notifyItemChanged(position);
}
}
}
private int findPositionOfApiarioInList(Apiario apiario) {
for (int i = 0; i < apiList.size(); i++) {
if (apiList.get(i).getNomeApiario().equals(apiario.getNomeApiario())) {
return i;
}
}
return -1;
}
}
public class ApiarioOnListAdapter extends RecyclerView.Adapter<ApiarioOnListAdapter.ApiarioViewHolder> {
private List<Apiario> apiarios;
private ApiarioClickListener listener;
public interface ApiarioClickListener {
void onEditClick(Apiario apiario);
void onDeleteClick(Apiario apiario);
}
public ApiarioOnListAdapter(List<Apiario> apiarios, ApiarioClickListener listener) {
this.apiarios = apiarios;
this.listener = listener;
}
public class ApiarioViewHolder extends RecyclerView.ViewHolder {
TextView textName;
TextView textEstado;
Button btnEdit;
Button btnDelete;
LinearLayout buttonContainer; // Add this line
public ApiarioViewHolder(@NonNull View itemView) {
super(itemView);
textName = itemView.findViewById(R.id.textName);
textEstado = itemView.findViewById(R.id.textEstado);
btnEdit = itemView.findViewById(R.id.btnEdit);
btnDelete = itemView.findViewById(R.id.btnDelete);
buttonContainer = itemView.findViewById(R.id.buttonContainer);
}
}
@NonNull
@Override
public ApiarioViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.apiario_item_list, parent, false);
return new ApiarioViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ApiarioViewHolder holder, int position) {
final Apiario apiario = apiarios.get(position);
holder.textName.setText("Nome: " + apiario.getNomeApiario());
holder.textEstado.setText("Estado: " + apiario.getEstado());
holder.btnEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Apiario selectedApiario = apiarios.get(holder.getAdapterPosition());
Log.d("TEST", "JV: SELECTED " + selectedApiario.getNomeApiario());
listener.onEditClick(selectedApiario);
}
});
holder.btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//listener.onDeleteClick(apiario);
int apiarioId = apiario.getID();
Log.d("TEST", "JV: ID A REMOVER " + apiarioId);
DatabaseReference apiarioRef = FirebaseDatabase.getInstance().getReference("apiario");
Query query = apiarioRef.orderByChild("id").equalTo(apiarioId);
query.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
for (DataSnapshot data : snapshot.getChildren()) {
String apiarioId = data.getKey();
Log.d("TEST", "JV: ENTRA NO FOR DA REMOÇAO " + apiarioId);
// Obtém a referência do banco de dados para o nó 'apiario' usando o ID do apiário
DatabaseReference apiarioToRemoveRef = apiarioRef.child(apiarioId);
// Remove o nó 'apiario' da base de dados
apiarioToRemoveRef.removeValue();
// Opcional: Remover o APIário da lista local
apiarios.remove(apiario);
// Notificar o adapter sobre a mudança nos dados
notifyDataSetChanged();
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
}
});
// Check if the state is "Pendente" and add a button dynamically
if (apiario.getEstado().equalsIgnoreCase("Pendente")) {
Button customButton = new Button(holder.itemView.getContext());
customButton.setText("Confirmar");
// Add the button to the layout
holder.buttonContainer.addView(customButton);
// Set a click listener for the custom button
customButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Handle button click
// You can perform specific actions based on the button click
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference("apiario");
Query query = mDatabase.orderByChild("id").equalTo(apiario.getID());
query.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
for (DataSnapshot data : snapshot.getChildren()){
String confirmID = data.getKey();
mDatabase.child(confirmID).child("estado").setValue("Aceite");
holder.buttonContainer.removeAllViews();
// Atualizar o estado na lista local
apiario.setEstado("Aceite");
// Notificar o adapter sobre a mudança nos dados
notifyDataSetChanged();
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
}
});
} else {
// If the state is not "Pendente", make sure to remove any previously added button
holder.buttonContainer.removeAllViews();
}
}
private void openEditApiarioActivity(Context context, Apiario apiario) {
Intent intent = new Intent(context, EditApiarioActivity.class);
intent.putExtra("apiario", apiario);
context.startActivity(intent);
}
@Override
public int getItemCount() {
return apiarios.size();
}
}
The problem is here:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apiarios_list);
RecyclerView recyclerView = findViewById(R.id.recyclerViewApiarios);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
this.apiList = new ArrayList<>(); // 👈
DatabaseReference mDatabaseQuery = FirebaseDatabase.getInstance().getReference("apiario");
Query query = mDatabaseQuery;
query.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
for(DataSnapshot dataSnapshot : snapshot.getChildren()){
Apiario apiario = dataSnapshot.getValue(Apiario.class);
apiList.add(apiario); // 👈
}
apiarioOnListAdapter = new ApiarioOnListAdapter(apiList, ApiariosListActivity.this);
recyclerView.setAdapter(apiarioOnListAdapter);
}
Since you use addValueEventListener
your onDataChange
will be called "immediately" with the initial data and then again whenever the data at query
changes. But you only create a new, empty apiList
once (in onCreate
), so each subsequent time that onDataChange
gets called, you're adding items to the already populated list.
A better implementation would be:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apiarios_list);
RecyclerView recyclerView = findViewById(R.id.recyclerViewApiarios);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
this.apiList = new ArrayList<>();
DatabaseReference mDatabaseQuery = FirebaseDatabase.getInstance().getReference("apiario");
Query query = mDatabaseQuery;
query.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
apiList.clear(); // 👈 Clear previous results
for(DataSnapshot dataSnapshot : snapshot.getChildren()){
Apiario apiario = dataSnapshot.getValue(Apiario.class);
apiList.add(apiario);
}
apiarioOnListAdapter = new ApiarioOnListAdapter(apiList, ApiariosListActivity.this);
recyclerView.setAdapter(apiarioOnListAdapter);
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
throw error.toException(); // 👈 Never ignore errors
}
});
If you don't need apiList
outside of onDataChange
(something I'd recommend), you could/should probably move it to be a local variable in onDataChange
. That would've avoided this problem, and will also prevent problems related to trying to access data before it is loaded/updated.