Search code examples
androidadapterandroid-recyclerviewandroid-room

How to update item in a recycler view using diff item callback?


I'm implementing a recycler view using room database library. I would like to list my items and that they could be edited by clicking. After re-submit the item, go back again to the list and show up the items + changes.

I'm currently using DiffUtil callback to compare the old and the new lists. I think the problem is here. My Dto item has 4 fields.

  • id title image arraylist

So when the user edits an item, the list should update the UI row (title, image or/and arraylist)

This is my Adapter.

private OnItemClickListener listener;

public ExercicioAdapter() {
    super(DIFF_CALLBACK);
}

private static final DiffUtil.ItemCallback<Exercicio> DIFF_CALLBACK = new DiffUtil.ItemCallback<Exercicio>() {
    @Override
    public boolean areItemsTheSame(@NonNull Exercicio oldExercicio, @NonNull Exercicio newExercicio) {
        return oldExercicio.getId() == newExercicio.getId();
    }

    @Override
    public boolean areContentsTheSame(@NonNull Exercicio oldExercicio, @NonNull Exercicio newExercicio) {
        return oldExercicio.getTitulo().equals(newExercicio.getTitulo()) &&
                Float.compare(oldExercicio.getMaxPeso(), newExercicio.getMaxPeso()) == 0;
    }
};

@NonNull
@Override
public MyHolderView onCreateViewHolder(@NonNull ViewGroup parent, int i) {
    View           view;
    LayoutInflater mInflater = LayoutInflater.from(parent.getContext());
    view = mInflater.inflate(R.layout.exercicios_item, parent, false);
    return new MyHolderView(view);
}

@Override
public void onBindViewHolder(@NonNull MyHolderView exercicioHolder, final int i) {
    Exercicio exercicio = getItem(i);

    // Mostrar a informacion do obxeto.
    ArrayList<Repeticion> repeticions = exercicio.getRepeticions();
    float                 peso_max    = 0f;
    if (repeticions != null && repeticions.size() > 0) {
        for (Repeticion rep : repeticions) {
            if (rep.getPeso() > peso_max) {
                peso_max = rep.getPeso();
            }
        }
    }
    exercicioHolder.nome.setText(exercicio.getTitulo());
    String peso_str = exercicioHolder.itemView.getContext().getString(R.string.peso_max);
    exercicioHolder.peso.setText(peso_str + " " + Float.toString(peso_max));

    if (exercicio.getImaxe() != null && exercicio.getImaxe().length != 0) {
        exercicioHolder.imx_exercicio.setImageBitmap(convertByteArrayToBitmap(exercicio.getImaxe()));
    }
}

public Exercicio getExercicioAt(int position) {
    return getItem(position);
}

public class MyHolderView extends RecyclerView.ViewHolder {
    TextView  nome;
    TextView  peso;
    ImageView imx_exercicio;
    CardView  exercicio_cardview;

    MyHolderView(@NonNull View itemView) {
        super(itemView);
        exercicio_cardview = itemView.findViewById(R.id.exercicio_cardview);
        nome = itemView.findViewById(R.id.nome);
        peso = itemView.findViewById(R.id.peso);
        imx_exercicio = itemView.findViewById(R.id.imx_exercicio);

        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int position = getAdapterPosition();
                if (listener != null && position != RecyclerView.NO_POSITION) {
                    listener.onItemClick(getItem(position));
                }
            }
        });
    }
}

public interface OnItemClickListener {
    void onItemClick(Exercicio exercicio);
}

public void setOnItemClickListener(OnItemClickListener listener) {
    this.listener = listener;
}

The problem: when an item is updated, changing the title for example, the list gets updated, but the eddited item disappears. If i end the activity and start it again, the item updated is there. So the problem must be the diff callback, but I'm not really able to make it work. Sorry for my English, this is not my main language, thanks all!


Solution

  • Most probably you are not properly using the DiffUtil functionality. You first need to calculate diff and then dispatch updates to adapter after which it will refresh your adapter with the new data. Use DiffUtil.Callback() and not DiffUtil.ItemCallback()

    DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
                @Override
                public int getOldListSize() {
                    // old list size, which is same as new in your case
                }
    
                @Override
                public int getNewListSize() {
                    // new list size, which is same as old in your case
                }
    
                @Override
                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition)
                }
    
                @Override
                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                }
    )
    
    result.dispatchUpdatesTo(this); // this parameter is your Recycler View's adapter
    

    You can read more about Diff Util here and here

    Let me know if you still have doubts. Thanks.