I was previously using RecyclerView Adapter and decided to switch to ListAdapter to make use of DiffUtils for the animations. Everything seem to go smoothly at first until I cleared my app from the task switcher (Memory). Once I opened my app again all the items were gone and this is with a SQLite database implemented. Turns out I have to add a new item unto the RecyclerView for the previous items to show up. This happened after I switched over to ListAdpater and I think that's the culprit. Any help would be appreciated. Thanks in advance.
My Adapter Class
public class CourseAdapter extends ListAdapter<Course, CourseAdapter.CourseHolder> {
private OnItemClickListener listener;
public CourseAdapter() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<Course> DIFF_CALLBACK = new DiffUtil.ItemCallback<Course>() {
#Override
public boolean areItemsTheSame(#NonNull Course oldItem, #NonNull Course newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull Course oldItem, #NonNull Course newItem) {
return oldItem.getName().equals(newItem.getName()) &&
oldItem.getBuilding().equals(newItem.getBuilding()) &&
oldItem.getRoom().equals(newItem.getRoom()) &&
oldItem.getDays().equals(newItem.getDays()) &&
oldItem.getStartTime().equals(newItem.getStartTime()) &&
oldItem.getEndTime().equals(newItem.getEndTime()) &&
oldItem.getProfName().equals(newItem.getProfName()) &&
oldItem.getEmail().equals(newItem.getEmail()) &&
oldItem.getProfBuilding().equals(newItem.getProfBuilding()) &&
oldItem.getProfRoom().equals(newItem.getProfRoom()) &&
oldItem.getProfDays().equals(newItem.getProfDays()) &&
oldItem.getProfStartTime().equals(newItem.getProfStartTime()) &&
oldItem.getProfEndTime().equals(newItem.getProfEndTime()) &&
oldItem.getColor() == newItem.getColor();
}
};
#NonNull
#Override
public CourseHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.course_item, parent, false);
return new CourseHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull CourseHolder holder, int position) {
Course currentCourse = getItem(position);
holder.card.setCardBackgroundColor(currentCourse.getColor());
holder.textViewName.setText(currentCourse.getName());
holder.textViewDays.setText(currentCourse.getDays());
String startTime = currentCourse.getStartTime();
String endTime = currentCourse.getEndTime();
if (startTime.isEmpty() && endTime.isEmpty()) {
holder.textViewTime.setText("");
} else {
holder.textViewTime.setText(startTime + "-" + endTime);
}
}
public Course getCourseAt(int position) {
return getItem(position);
}
class CourseHolder extends RecyclerView.ViewHolder {
private TextView textViewName;
private TextView textViewDays;
private TextView textViewTime;
private MaterialCardView card;
public CourseHolder(#NonNull View itemView) {
super(itemView);
textViewName = itemView.findViewById(R.id.text_view_name);
textViewDays = itemView.findViewById(R.id.text_view_days);
textViewTime = itemView.findViewById(R.id.text_view_time);
card = itemView.findViewById(R.id.card_view_courses);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if (listener != null && position != RecyclerView.NO_POSITION) {
listener.onItemClick(getItem(position), position, textViewName);
}
}
});
}
}
public interface OnItemClickListener {
void onItemClick(Course course, int position, TextView title);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
}
In Main Activity
RecyclerView recyclerView = findViewById(R.id.recycler_view_courses);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
CourseAdapter adapter = new CourseAdapter();
recyclerView.setAdapter(adapter);
viewModel = ViewModelProviders.of(this).get(ViewModel.class);
viewModel.getAllCourses().observe(this, new Observer<List<Course>>() {
#Override
public void onChanged(List<Course> courses) {
adapter.submitList(courses);
}
});
Related
I have attached a picture to help you understand my problem.
I'm making an Workout app.
When the button is pressed, the routine is added
In the added routine, it can use the button again to add detailed data (set, lbs, reps).
So, I am using a two type RecyclerView (except footer).
I used DiffUtil to update the items being added.
I found a strange phenomenon while experimenting for testing. (See photo)
It wasn't an error, so I can't figure out what's wrong with it.
After entering the data, when i added a number of detailed items, the value of the data entered first appeared in the items added later.
And if i scrolled after many items were added, the data moved randomly.
However, what is expected is this phenomenon when you add a lot of items or scroll.
What's wrong?
RoutineAdapter.java
public class RoutineAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final static int TYPE_ROUTINE = 1;
final static int TYPE_ROUTINE_DETAIL = 2;
final static int TYPE_ROUTINE_FOOTER = 3;
private Context context;
private List<Object> mItems = new ArrayList<>();
OnRoutineItemClickListener routinelistener;
OnRoutineAddClickListener routineAddListener;
public void updateRoutineList(List<Object> newRoutineList) {
final RoutineDiffUtil diffCallback = new RoutineDiffUtil(this.mItems, newRoutineList);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
this.mItems.clear();
this.mItems.addAll(newRoutineList);
diffResult.dispatchUpdatesTo(this);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View itemView;
if(viewType == TYPE_ROUTINE){
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder(itemView);
}
else if(viewType == TYPE_ROUTINE_DETAIL){
itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailViewHolder(itemView);
}
else {
itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item, parent, false);
return new RoutineAddFooterViewHolder(itemView);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object obj;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
obj = mItems.get(position);
setRoutineData((RoutineViewHolder) holder, (RoutineModel) obj);
break;
case TYPE_ROUTINE_DETAIL:
obj = mItems.get(position);
RoutineDetailModel item = (RoutineDetailModel) obj;
((RoutineDetailViewHolder) holder).setDetailItem(item);
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
#Override
public int getItemCount() {
if(mItems == null)
return -1;
return mItems.size() + 1; // for footer
}
#Override
public int getItemViewType(int position) {
if(position == mItems.size()) {
return TYPE_ROUTINE_FOOTER;
}
else {
Object obj = mItems.get(position);
if(obj instanceof RoutineModel) {
return TYPE_ROUTINE;
}
else {
// obj instanceof RoutineDetailModel
return TYPE_ROUTINE_DETAIL;
}
}
}
// add routine interface
public interface OnRoutineAddClickListener {
public void onAddRoutineClick();
}
public void setOnAddRoutineClickListener(OnRoutineAddClickListener listener) {
this.routineAddListener = listener;
}
// details add / remove interface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
}
public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
this.routinelistener = listener;
}
private class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public Button comment;
public RoutineViewHolder(#NonNull View itemView) {
super(itemView);
initViews();
addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onAddBtnClicked(getAdapterPosition());
}
});
deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onDeleteBtnClicked(getAdapterPosition());
}
});
comment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onWritingCommentBtnClicked(getAdapterPosition());
}
});
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
public TextView set;
public TextView weight;
public RoutineDetailViewHolder(#NonNull View itemView) {
super(itemView);
initViews();
}
private void initViews() {
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
private void setDetailItem(RoutineDetailModel item) {
set.setText(item.getSet().toString() + "set");
}
}
}
RoutineDiffUtil.java
public class RoutineDiffUtil extends DiffUtil.Callback {
private List<Object> oldRoutineList;
private List<Object> newRoutineList;
public RoutineDiffUtil(List<Object> oldRoutineList, List<Object> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
#Override
public int getOldListSize() {
return oldRoutineList.size();
}
#Override
public int getNewListSize() {
return newRoutineList.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
Object oldObj = oldRoutineList.get(oldItemPosition);
Object newObj = newRoutineList.get(newItemPosition);
if (oldObj instanceof RoutineModel && newObj instanceof RoutineModel) {
return ((RoutineModel) oldObj).id == ((RoutineModel) newObj).id;
}
else if (oldObj instanceof RoutineDetailModel && newObj instanceof RoutineDetailModel) {
return ((RoutineDetailModel) oldObj).id == ((RoutineDetailModel) newObj).id;
}
else if(oldObj instanceof RoutineModel && newObj instanceof RoutineDetailModel) {
return false;
}
else {
return false;
}
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return (oldRoutineList.get(oldItemPosition)).equals(newRoutineList.get(newItemPosition));
}
}
RoutineDetailModel.java
public class RoutineDetailModel {
public int id;
private int set = 1;
private int weight;
public RoutineDetailModel() {
Random random = new Random();
this.id = random.nextInt();
}
public RoutineDetailModel(int set) {
Random random = new Random();
this.id = random.nextInt();
this.set = set+1;
}
public Integer getSet() {
return set;
}
public int getId() {
return id;
}
#Override
public int hashCode() {
return Objects.hash(set, weight);
}
#Override
public boolean equals(#Nullable Object obj) {
if(obj != null && obj instanceof RoutineDetailModel) {
RoutineDetailModel model = (RoutineDetailModel) obj;
if(this.id == model.getId()) {
return true;
}
}
return false;
}
}
First of all, you should use ListAdapter class with diff util. The problem with your adapter is that, Recycler view recycles views again and again. That is to say that when you entered a text for your first item, this item is used for other views. To solve it after any text change you should keep this text in your model class then you should set this text to the field in onBind() method. To sum up, recycler view uses same view for different items so any data related to any item should be kept in a model and the model should be set to the view in onBind().
I created a recyclerview with a StaggeredGridLayoutManager as LayoutManager.
I wanted to implement a multiple selection thanks to the recyclerview-selection library but when I make a long click on a certain item, it sometimes selects 2. (I am using a DB to retrieve the data)
This is my main code:
GridRecyclerView notesRecyclerView = binding.noteRecyclerView;
notesRecyclerView.setHasFixedSize(true);
adapter = new NoteAdapterTest();
notesRecyclerView.setAdapter(adapter);
SelectionTracker<Long> tracker = new SelectionTracker.Builder<>("mySelection", notesRecyclerView,
new StableIdKeyProvider(notesRecyclerView),
new NoteDetailsLookup(notesRecyclerView),
StorageStrategy.createLongStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything()).build();
adapter.setSelectionTracker(tracker);
My Adapter:
private SelectionTracker<Long> selectionTracker;
private static final DiffUtil.ItemCallback<Note> DIFF_CALLBACK = new DiffUtil.ItemCallback<Note>() {
#Override
public boolean areItemsTheSame(#NonNull Note oldItem, #NonNull Note newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull Note oldItem, #NonNull Note newItem) {
return oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getContent().equals(newItem.getContent());
}
};
public NoteAdapterTest() {
super(DIFF_CALLBACK);
setHasStableIds(true);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
AdapterNoteBinding binding = AdapterNoteBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new NoteHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Note note = getItem(position);
((NoteHolder) holder).bind(note, selectionTracker.isSelected((long) position));
}
public static class NoteHolder extends RecyclerView.ViewHolder {
private final AdapterNoteBinding binding;
public NoteHolder(#NonNull AdapterNoteBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public final void bind(Note note, boolean isActive) {
Context context = binding.getRoot().getContext();
itemView.setActivated(isActive);
if (isActive) {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.blue));
} else {
binding.noteTitle.setText(note.getTitle());
}
}
public ItemDetailsLookup.ItemDetails<Long> getItemDetails() {
return new ItemDetailsLookup.ItemDetails<Long>() {
#Override
public int getPosition() {
return getAdapterPosition();
}
#Nullable
#Override
public Long getSelectionKey() {
return getItemId();
}
};
}
}
#Override
public long getItemId(int position) {
return position;
}
public void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
this.selectionTracker = selectionTracker;
}
}
My lookup:
public class NoteDetailsLookup extends ItemDetailsLookup<Long> {
private final RecyclerView recyclerView;
public NoteDetailsLookup(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
#Nullable
#Override
public ItemDetails<Long> getItemDetails(#NonNull MotionEvent e) {
View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(view);
if (viewHolder instanceof NoteAdapterTest.NoteHolder) {
return ((NoteAdapterTest.NoteHolder) viewHolder).getItemDetails();
}
}
return null;
}
}
How do you know that 2 items are selected? What the selection tracker returns?
Try to set background tint in your view holder always:
if (isActive) {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.blue));
} else {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.white)); // your default color
}
It may be just a view state issue.
I am developing the Workout log app now.
Two items (routine and routine detail) are expressed using one recycler view and adapter.
If i click the Add Routine button, a routine item is added, and the routine basically has one routine detail item.
Routine items have buttons to add or delete routine detail items.
I used DiffUtil to update the item.
In areItemsTheSame(), I used hashCode to compare oldList and newList.
However, there is a problem of unknown cause when adding or deleting items. (Not an error).
If i click the delete button after adding the routine item and the detail item, it works well at first.
The routine detail buttons of the next routine item cannot be added or deleted unless the delete button of the previous routine item is pressed.
If the delete button of the previous routine item is pressed and then the button of the next item is pressed, the addition or deletion is performed.
Why is this?
This happens when you use hashCode comparison.
However, with equals() this does not happen and works fine.
Instead, every time an item is added or deleted, the entire item is updated with blinking.
How did I have to define the DiffUtil class?
CODE
RoutineModel.java
public class RoutineModel {
private ArrayList<RoutineDetailModel> routineDetailList;
private String routine;
public RoutineModel(String routine) {
this.routine = routine;
}
public void addDetail(RoutineDetailModel item) {
if(routineDetailList == null) {
routineDetailList = new ArrayList<>();
}
this.routineDetailList.add(item);
}
public ArrayList<RoutineDetailModel> getDetailItemList() {
return routineDetailList;
}
public int getDetailItemSize() {
return routineDetailList.size();
}
public String getRoutine() {
return routine;
}
public void removeDetails(int index) throws Exception {
this.routineDetailList.remove(index);
}
#Override
public int hashCode() {
return Objects.hash(routineDetailList, routine);
}
#Override
public boolean equals(#Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
RoutineModel that = (RoutineModel) obj;
return Objects.equals(routine, that.routine) && Objects.equals(routineDetailList, that.routineDetailList);
}
}
RoutineAdapter.java
public class RoutineAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
final static int TYPE_ROUTINE = 1;
final static int TYPE_ROUTINE_DETAIL = 2;
private Context context;
private List<Object> mItems = new ArrayList<>();
OnRoutineItemClickListener listener;
public void updateRoutineList(List<Object> newRoutineList) {
final RoutineDiffUtil diffCallback = new RoutineDiffUtil(this.mItems, newRoutineList);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
this.mItems.clear();
this.mItems.addAll(newRoutineList);
diffResult.dispatchUpdatesTo(this);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
if (viewType == TYPE_ROUTINE) {
View itemView = LayoutInflater.from(context).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder(itemView);
}
View itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object object = mItems.get(position);
if(object instanceof RoutineModel) {
setRoutineData((RoutineViewHolder) holder, (RoutineModel) object, position);
}
else if(object instanceof RoutineDetailModel) {
}
}
private void setRoutineData(RoutineViewHolder holder, RoutineModel routineItem, int position){
holder.routine.setText(routineItem.getRoutine());
holder.addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null) listener.OnAddBtnClick(position);
}
});
holder.deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null) listener.OnDeleteBtnClick(position);
}
});
}
public Object getRoutineItem(int position) {
if(mItems == null || position < 0 || position >= mItems.size())
return null;
return mItems.get(position);
}
#Override
public int getItemCount() {
if(mItems == null)
return -1;
return mItems.size();
}
#Override
public int getItemViewType(int position) {
Object obj = mItems.get(position);
if(obj instanceof RoutineModel) {
return TYPE_ROUTINE;
}
return TYPE_ROUTINE_DETAIL;
}
// detail add,delete click interface
public interface OnRoutineItemClickListener {
public void OnAddBtnClick(int curRoutinePos);
public void OnDeleteBtnClick(int curRoutinePos);
}
public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
this.listener = listener;
}
public class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public RoutineViewHolder(#NonNull View itemView) {
super(itemView);
routine = itemView.findViewById(R.id.routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
}
public class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
public TextView set;
public TextView weight;
public RoutineDetailViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
}
}
RoutineDiffUtil.java
public class RoutineDiffUtil extends DiffUtil.Callback {
private List<Object> oldRoutineList;
private List<Object> newRoutineList;
public RoutineDiffUtil(List<Object> oldRoutineList) {
this.oldRoutineList = oldRoutineList;
newRoutineList = new ArrayList<>();
}
public RoutineDiffUtil(List<Object> oldRoutineList, List<Object> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
#Override
public int getOldListSize() {
return oldRoutineList.size();
}
#Override
public int getNewListSize() {
return newRoutineList.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
// boolean result = oldRoutineList.equals(newRoutineList); // work well
boolean result = oldRoutineList.get(oldItemPosition).hashCode() == newRoutineList.get(newItemPosition).hashCode();
return result;
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldRoutineList.get(oldItemPosition).equals(newRoutineList.get(newItemPosition));
}
}
MainActivity.java
public class WriteRoutineActivity extends AppCompatActivity {
Button add_routine_btn;
TextView title;
RecyclerView routine_rv;
LinearLayoutManager routineLayoutManger;
RoutineAdapter routineAdapter;
List<RoutineModel> items;
List<String> titleData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
initViews();
setPageTitle(getIntent());
setRoutineRecyclerview();
items = new ArrayList<>();
routineAdapter = new RoutineAdapter();
routine_rv.setAdapter(routineAdapter);
add_routine_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
routineDialog.show(getSupportFragmentManager(), "RoutineListDialog");
}
});
routineAdapter.setOnRoutineClickListener(new RoutineAdapter.OnRoutineItemClickListener() {
#Override
public void OnAddBtnClick(int routinePos) {
Object obj = routineAdapter.getRoutineItem(routinePos);
if(obj instanceof RoutineModel) {
RoutineModel item = (RoutineModel) obj;
item.addDetail(new RoutineDetailModel());
routineAdapter.updateRoutineList(getDataToBeDisplayed());
}
}
#Override
public void OnDeleteBtnClick(int routinePos) {
Object item = routineAdapter.getRoutineItem(routinePos);
if(item instanceof RoutineModel) {
RoutineModel routineModel = (RoutineModel) item;
if(routineModel.getDetailItemSize() > 1) {
try {
routineModel.removeDetails(routineModel.getDetailItemSize() - 1);
} catch (Exception e) {
e.printStackTrace();
}
}
else { // if delete item exists only one
items.remove(routineModel);
}
routineAdapter.updateRoutineList(getDataToBeDisplayed());
}
}
});
}
public void addRoutine(String routine) {
RoutineModel routineModel = new RoutineModel(routine);
RoutineDetailModel routineDetailModel = new RoutineDetailModel();
routineModel.addDetail(routineDetailModel);
items.add(routineModel);
routineAdapter.updateRoutineList(getDataToBeDisplayed());
}
private List<Object> getDataToBeDisplayed() {
List<Object> mixedList = new ArrayList<>();
for(RoutineModel rm: items){
mixedList.add(rm);
if(rm.getDetailItemList() != null && rm.getDetailItemSize() > 0){
for(RoutineDetailModel rmdetilas: rm.getDetailItemList()){
mixedList.add(rmdetilas);
}
}
}
return mixedList;
}
}
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!
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.
Actually in my RecyclerView i'm able to create Child items by programmatically adding view and TextView.
I even have a onClickListener for my parent item from the same RecyclerView and i've added a onClick method for my Child view's but now my main issue is how can i use the Child onClick in my Activity?
Here is my Adapter code
public class AdapterPTERM extends RecyclerView.Adapter<AdapterPTERM.ExampleViewHolder> {
private List<ItemPTERM> mExampleList;
private final LayoutInflater mInflater;
private OnItemClickListener mListener;
// ONCLICK FOR THE CHILD ITEMS
private View.OnClickListener varientClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.getTag()!= null){
int position = (int) v.getTag();
}
}
};
public interface OnItemClickListener{
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener = listener;
}
#NonNull
#Override
public ExampleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_pterm,parent,false);
return new ExampleViewHolder(v,mListener);
}
AdapterPTERM(Context context, List<ItemPTERM> exampleList){
mExampleList = exampleList;
mInflater = LayoutInflater.from(context);
}
#Override
public void onBindViewHolder(#NonNull final ExampleViewHolder holder, final int position) {
final ItemPTERM item = mExampleList.get(position);
holder.desc.setText(item.getBtnName());
holder.qta.setText(String.valueOf(item.getQuant()));
holder.variantsContainer.removeAllViews();
// CREATING CHILD ITEM
List<Variant> variants = item.getVariants();
if (variants != null && variants.size() > 0){
for(Variant v : variants){
View vView = mInflater.inflate(R.layout.variant_layout,holder.variantsContainer,false);
TextView nameTV = vView.findViewById(R.id.variant_name);
nameTV.setText(v.getName());
vView.setTag(position);
vView.setOnClickListener(varientClickListener);
holder.variantsContainer.addView(vView);
}
}
// NOT IMPORTANT (CHANGING FIELDS COLOR)
if(position % 2 == 0 ){
holder.itemView.setBackgroundColor(Color.parseColor("#C0C0C0"));
}else if(position % 2 == 1){
holder.itemView.setBackgroundColor(Color.parseColor("#D3D3D3"));
}
}
#Override
public int getItemCount() {
return mExampleList.size();
}
public class ExampleViewHolder extends RecyclerView.ViewHolder {
public TextView desc;
public TextView qta;
private LinearLayout variantsContainer;
ExampleViewHolder(View itemView, final OnItemClickListener listener) {
super(itemView);
desc = itemView.findViewById(R.id.Desc);
qta = itemView.findViewById(R.id.Qta);
variantsContainer = itemView.findViewById(R.id.ll_child_items);
// onClick method for Parent Item
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null){
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
listener.onItemClick(position);
}
}
}
});
}
}
public void removeItem(int position) {
mExampleList.remove(position);
notifyItemRemoved(position);
}
public void removeVariant(int position,int positionbtn){
mExampleList.get(positionbtn).getVariants().remove(position);
notifyDataSetChanged();
}
}
While here is my code from the Activity where i build my RecyclerView and here i'm using the onClick for the parent for make appear an AlertDialog but i would be able to make appear that AlertDialog by clicking onChild instead of parent.
public void buildTopRecyclerView() {
mRecyclerViewTOP = findViewById(R.id.listView);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
exampleAdapter = new AdapterPTERM(this,dummyDataItems);
mRecyclerViewTOP.setLayoutManager(mLayoutManager);
mRecyclerViewTOP.setAdapter(exampleAdapter);
mRecyclerViewTOP.setHasFixedSize(true);
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
if (direction == ItemTouchHelper.RIGHT) {
customAllertDelete(position);
exampleAdapter.notifyItemChanged(position);
}
if (direction == ItemTouchHelper.LEFT) {
customAllertQuantity(position);
exampleAdapter.notifyItemChanged(position);
}
}
}).attachToRecyclerView(mRecyclerViewTOP);
exampleAdapter.setOnItemClickListener(new AdapterPTERM.OnItemClickListener() {
#Override
public void onItemClick(int position) {
if (dummyDataItems.get(position).getVariants() != null) {
customAlertVar(position);
}else {
//
}
}
});
}
SOLVED
I just had to remove
// ONCLICK FOR THE CHILD ITEMS
private View.OnClickListener varientClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.getTag()!= null){
int position = (int) v.getTag();
}
}
};
From my adapter as it was handling another click and was blocking the AlertDialog and even removed the
vView.setTag(position);
vView.setOnClickListener(varientClickListener);