问题
My app uses a firebase RecyclerAdapter to display "plant" entities in an activity. I made it so that if I longclick on an entity displayed in Cardview format using a ViewHolder and the RecyclerAdapter it will prompt to perform a delete and delete the plant entity + image on the firebase database and storage. Deleting and adding plants work without a hitch on the backend as I can confirm this on my firebase database.
However, when I delete a "plant" entity and add a new one, the CardView shows the previous or another image. Deleting the app and reinstalling it seems to fix the problem, because of this I think it might have to do with the local cache.
- PlantActivity.java (where the plants are loaded) I think this might be solved if there was some way to refresh the activity in some way or the RecyclerAdapter... although i've tried many things already
public class PlantActivity extends AppCompatActivity {
private static final int ADD_REQUEST = 101;
private static final String TAG = "PlantActivityView";
private DatabaseReference mDatabaseReference;
private FirebaseRecyclerAdapter plantAdapter;
private PlantDAO mPlantDAO;
private UserDAO mUserDAO;
private CoordinatorLayout coordinatorLayout;
@Override
protected void onStart() {
super.onStart();
if(!mUserDAO.isLoggedIn()){
finish();
startActivity(new Intent(PlantActivity.this, LoginActivity.class));
}
plantAdapter.startListening();
}
@Override
protected void onStop() {
super.onStop();
plantAdapter.stopListening();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_plant_view);
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mDatabaseReference = FirebaseDatabase.getInstance().getReference();
mPlantDAO = new PlantDAO(mDatabaseReference);
mUserDAO = new UserDAO();
//make custom appcompat toolbar to replace actionbar and add logout item
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
coordinatorLayout = findViewById(R.id.plant_coordinator);
providePlantsOfCurrentUser();
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(),PlantAddActivity.class);
startActivityForResult(intent, ADD_REQUEST);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.menuLogout:
mUserDAO.getAuth().signOut();
finish();
startActivity(new Intent(PlantActivity.this,LoginActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Snackbar GoodResultSnackbar = Snackbar.make(coordinatorLayout,"PLANT ADDED",Snackbar.LENGTH_SHORT);
Snackbar BadResultSnackbar = Snackbar.make(coordinatorLayout,"PLANT ADD FAILED",Snackbar.LENGTH_SHORT);
if(requestCode == ADD_REQUEST){
if(resultCode == Activity.RESULT_OK){
GoodResultSnackbar.show();
} else if(resultCode == Activity.RESULT_CANCELED){
BadResultSnackbar.show();
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void providePlantsOfCurrentUser(){
FirebaseRecyclerOptions<Plant> options = new FirebaseRecyclerOptions.Builder<Plant>().setQuery(mPlantDAO.currentUserPlantsQuery(), Plant.class).build();
plantAdapter = new FirebaseRecyclerAdapter<Plant,PlantViewHolder>(options) {
@NonNull
@Override
public PlantViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cardview_item_plant, parent, false);
return new PlantViewHolder(view);
}
@Override
protected void onBindViewHolder(@NonNull final PlantViewHolder holder, final int position, @NonNull final Plant model) {
StorageReference localstorage = FirebaseStorage.getInstance().getReferenceFromUrl(model.getImageLocation());
String plantText = /*model.getPlantID() + ": " + */ model.getPlanttype();
holder.tv_plant_name.setText(plantText);
//image implementation
GlideApp.with(getApplicationContext()).load(localstorage).into(holder.img_plant_thumbnail);
holder.dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which){
case DialogInterface.BUTTON_POSITIVE:
mPlantDAO.deletePlant(model.getPlantID());
plantAdapter.notifyDataSetChanged();
break;
case DialogInterface.BUTTON_NEGATIVE:
//Return
break;
}
}
};
holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//passing data with intent to PlantDetailActivity instance
Intent intent = new Intent(getApplicationContext(), PlantDetailActivity.class);
intent.putExtra("plantID", model.getPlantID());
intent.putExtra("planttype", model.getPlanttype());
//image implementation
intent.putExtra("image_url", model.getImageLocation());
//start the activity
startActivity(intent);
}
});
holder.cardView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
showDeleteDialog(holder);
return true;
}
});
}
};
RecyclerView recyclerView = findViewById(R.id.recyclerview_id);
recyclerView.setAdapter(plantAdapter);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
}
private void showDeleteDialog(PlantViewHolder holder){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Delete this plant?").setPositiveButton("Yes", holder.dialogClickListener)
.setNegativeButton("Cancel", holder.dialogClickListener).show();
}
}
回答1:
The position and model are final on the onBindViewHolder method
The viewholder can be final, but not the data or the position
Use holder.getAdapterPosition() to get the position inside annonymous interfaces like click listeners
And then get the data using getItem() method from the adapter class
回答2:
The last answer by @cutiko definitely helped! But I think I realize the problem. I was saving my plantID string attribute (of new Plant object) in this fashion: "plant_" + count() of "plants" in database + "_" + ID of the current user + ".jpg"
The problem this causes is that when I delete a plant entity and add another this could cause 2 plant entities in the database to have the same ID which causes strange behavior.
I will solve this by instead of deleting a plant from the database, changing a new attribute's("active") to false and not showing these in the FirebaseRecyclerAdapter
来源:https://stackoverflow.com/questions/53837296/firebase-recycleradapter-displaying-wrong-information-android