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;
}
}