I am trying to make a simple shopping list kind of app, where each item is displayed in a RecyclerView with a CheckBox and two ImageButtons. When the CheckBox is checked, I want to Display the item texts as strike-through.
Here is my RecyclerView Adapter layout file:
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/adapterCardItemDisplay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/adapterTvItemName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/adapterTvItemNameBeg"
android:textSize="18sp"
android:layout_margin="1dp"
/>
<TextView
android:id="@+id/adapterTvItemQuantity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/adapterTvItemQuantityBeg"
android:textSize="18sp"
android:layout_margin="1dp"
/>
<TextView
android:id="@+id/adapterTvItemColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/adapterTvItemColorBeg"
android:textSize="18sp"
android:layout_margin="1dp"
/>
<TextView
android:id="@+id/adapterTvItemSize"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/adapterTvItemSizeBeg"
android:textSize="18sp"
android:layout_margin="1dp"
/>
<TextView
android:id="@+id/adapterTvItemRemarks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/adapterTvItemRemarksBeg"
android:textSize="18sp"
android:layout_margin="1dp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="15dp"
>
<CheckBox
android:id="@+id/adapterChkItemChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="start"
>
<ImageButton
android:id="@+id/adapterImgBtnEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFFCFC"
android:src="@android:drawable/ic_menu_edit"
android:layout_marginEnd="10dp"/>
<ImageButton
android:id="@+id/adapterImgBtnDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F8F4F4"
android:src="@android:drawable/ic_delete" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
And here is my Adapter Java File :
public class ItemDisplayAdapter extends RecyclerView.Adapter <ItemDisplayAdapter.ViewHolder> {
private static final String TAG = "ItemDisplayAdapter";
private CardView adapterCardItemDisplay;
private TextView adapterTvItemName;
private TextView adapterTvItemQuantity;
private TextView adapterTvItemColor;
private TextView adapterTvItemSize;
private TextView adapterTvItemRemarks;
private CheckBox adapterChkItemChecked;
private ImageButton adapterImgBtnEdit;
private ImageButton adapterImgBtnDelete;
private Context context;
private ArrayList<Item> adapterItemArrayList;
public ItemDisplayAdapter(Context context, ArrayList<Item> adapterItemArrayList) {
this.context = context;
this.adapterItemArrayList = adapterItemArrayList;
}
private void checkAnItem (boolean isChecked,int position) {
if (isChecked) {
Log.d(TAG, "checkAnItem: here");
adapterTvItemName.setPaintFlags(adapterTvItemName.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
adapterTvItemQuantity.setPaintFlags(adapterTvItemQuantity.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
adapterTvItemColor.setPaintFlags(adapterTvItemColor.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
adapterTvItemSize.setPaintFlags(adapterTvItemSize.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
adapterTvItemRemarks.setPaintFlags(adapterTvItemRemarks.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}else {
adapterTvItemName.setPaintFlags(adapterTvItemName.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
adapterTvItemQuantity.setPaintFlags(adapterTvItemQuantity.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
adapterTvItemColor.setPaintFlags(adapterTvItemColor.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
adapterTvItemSize.setPaintFlags(adapterTvItemSize.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
adapterTvItemRemarks.setPaintFlags(adapterTvItemRemarks.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
//invalidateViews();
}
private void invalidateViews () {
adapterTvItemName.invalidate();
adapterTvItemQuantity.invalidate();
adapterTvItemColor.invalidate();
adapterTvItemSize.invalidate();
adapterTvItemRemarks.invalidate();
}
private void editAnItem () {
}
private void deleteAnItem () {
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_item_list_view,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
//Log.d(TAG, "onBindViewHolder: "+position);
Item item = adapterItemArrayList.get(position);
adapterCardItemDisplay.setTag(item.getId());
String nameString = context.getResources().getString(R.string.adapterTvItemNameBeg)+
" "+item.getName();
adapterTvItemName.setText(nameString);
String quantityString = context.getResources().getString(R.string.adapterTvItemQuantityBeg)+
" "+item.getQuantity();
adapterTvItemQuantity.setText(quantityString);
if (item.getColor().isEmpty()) {
adapterTvItemColor.setVisibility(View.GONE);
} else {
adapterTvItemColor.setVisibility(View.VISIBLE);
String colorString = context.getResources().getString(R.string.adapterTvItemColorBeg) +
" " + item.getColor();
adapterTvItemColor.setText(colorString);
}
if (item.getSize() == 0) {
adapterTvItemSize.setVisibility(View.GONE);
} else {
adapterTvItemSize.setVisibility(View.VISIBLE);
String sizeString = context.getResources().getString(R.string.adapterTvItemSizeBeg)+
" "+item.getSize();
adapterTvItemSize.setText(sizeString);
}
if (item.getRemarks().isEmpty()) {
adapterTvItemRemarks.setVisibility(View.GONE);
} else {
adapterTvItemRemarks.setVisibility(View.VISIBLE);
String remarksString = context.getResources().getString(R.string.adapterTvItemRemarksBeg)+
" "+item.getRemarks();
adapterTvItemRemarks.setText(remarksString);
}
adapterChkItemChecked.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.d(TAG, "onCheckedChanged: here");
checkAnItem(isChecked,position);
notifyItemChanged(position);
adapterChkItemChecked.setChecked(isChecked);
}
});
adapterImgBtnEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editAnItem();
}
});
adapterImgBtnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
deleteAnItem();
}
});
}
@Override
public int getItemCount() {
return adapterItemArrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
adapterCardItemDisplay = itemView.findViewById(R.id.adapterCardItemDisplay);
adapterTvItemName = itemView.findViewById(R.id.adapterTvItemName);
adapterTvItemQuantity = itemView.findViewById(R.id.adapterTvItemQuantity);
adapterTvItemColor = itemView.findViewById(R.id.adapterTvItemColor);
adapterTvItemSize = itemView.findViewById(R.id.adapterTvItemSize);
adapterTvItemRemarks = itemView.findViewById(R.id.adapterTvItemRemarks);
adapterChkItemChecked = itemView.findViewById(R.id.adapterChkItemChecked);
adapterImgBtnEdit = itemView.findViewById(R.id.adapterImgBtnEdit);
adapterImgBtnDelete = itemView.findViewById(R.id.adapterImgBtnDelete);
}
}
}
I am unable to get the expected behavior. Clicking on a CheckBox, doesn't give the strike-through effect on that item, maybe clicking again makes change to another item, (say clicking checkbox on item 0 actually makes the strike-through change on item 4 as such), sometimes i get the strike-through effect on repeated clicks but at that time the checkbox doesn't stay checked. So I am at a loss how to achieve it.
Invalidating the views didn't work, neither did the notifyItemChanged(position) method.
Any Suggestions?
UPDATE: so i followed Daniel's suggestion, and it got rid of 50% of the Problem, but the checkbox keeps getting unchecked after the view update, i suppose that's an expected behavior since the view is getting updated, that's why i had the code in the 'CheckedChangeListener' that sets the checkBox with the 'isChecked' parameter after 'notifyItemChanged(position)' but it doesn't seem to work properly. What am I doing wrong?
Updated Code for the adapter class:
public class ItemDisplayAdapter extends RecyclerView.Adapter <ItemDisplayAdapter.ViewHolder> {
private static final String TAG = "ItemDisplayAdapter";
private Context context;
private ArrayList<Item> adapterItemArrayList;
public ItemDisplayAdapter(Context context, ArrayList<Item> adapterItemArrayList) {
this.context = context;
this.adapterItemArrayList = adapterItemArrayList;
}
private void checkAnItem (ViewHolder holder,boolean isChecked,int position) {
if (isChecked) {
Log.d(TAG, "checkAnItem: here");
holder.adapterTvItemName.setPaintFlags(holder.adapterTvItemName.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.adapterTvItemQuantity.setPaintFlags(holder.adapterTvItemQuantity.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.adapterTvItemColor.setPaintFlags(holder.adapterTvItemColor.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.adapterTvItemSize.setPaintFlags(holder.adapterTvItemSize.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.adapterTvItemRemarks.setPaintFlags(holder.adapterTvItemRemarks.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}else {
holder.adapterTvItemName.setPaintFlags(holder.adapterTvItemName.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
holder.adapterTvItemQuantity.setPaintFlags(holder.adapterTvItemQuantity.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
holder.adapterTvItemColor.setPaintFlags(holder.adapterTvItemColor.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
holder.adapterTvItemSize.setPaintFlags(holder.adapterTvItemSize.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
holder.adapterTvItemRemarks.setPaintFlags(holder.adapterTvItemRemarks.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
//invalidateViews();
}
private void invalidateViews (ViewHolder holder) {
holder.adapterTvItemName.invalidate();
holder.adapterTvItemQuantity.invalidate();
holder.adapterTvItemColor.invalidate();
holder.adapterTvItemSize.invalidate();
holder.adapterTvItemRemarks.invalidate();
}
private void editAnItem () {
}
private void deleteAnItem () {
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_item_list_view,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
//Log.d(TAG, "onBindViewHolder: "+position);
Item item = adapterItemArrayList.get(position);
holder.adapterCardItemDisplay.setTag(item.getId());
String nameString = context.getResources().getString(R.string.adapterTvItemNameBeg)+
" "+item.getName();
holder.adapterTvItemName.setText(nameString);
String quantityString = context.getResources().getString(R.string.adapterTvItemQuantityBeg)+
" "+item.getQuantity();
holder.adapterTvItemQuantity.setText(quantityString);
if (item.getColor().isEmpty()) {
holder.adapterTvItemColor.setVisibility(View.GONE);
} else {
holder.adapterTvItemColor.setVisibility(View.VISIBLE);
String colorString = context.getResources().getString(R.string.adapterTvItemColorBeg) +
" " + item.getColor();
holder.adapterTvItemColor.setText(colorString);
}
if (item.getSize() == 0) {
holder.adapterTvItemSize.setVisibility(View.GONE);
} else {
holder.adapterTvItemSize.setVisibility(View.VISIBLE);
String sizeString = context.getResources().getString(R.string.adapterTvItemSizeBeg)+
" "+item.getSize();
holder.adapterTvItemSize.setText(sizeString);
}
if (item.getRemarks().isEmpty()) {
holder.adapterTvItemRemarks.setVisibility(View.GONE);
} else {
holder.adapterTvItemRemarks.setVisibility(View.VISIBLE);
String remarksString = context.getResources().getString(R.string.adapterTvItemRemarksBeg)+
" "+item.getRemarks();
holder.adapterTvItemRemarks.setText(remarksString);
}
holder.adapterChkItemChecked.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.d(TAG, "onCheckedChanged: here");
checkAnItem(holder,isChecked,position);
notifyItemChanged(position);
holder.adapterChkItemChecked.setChecked(isChecked);
}
});
holder.adapterImgBtnEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editAnItem();
}
});
holder.adapterImgBtnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
deleteAnItem();
}
});
}
@Override
public int getItemCount() {
return adapterItemArrayList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private CardView adapterCardItemDisplay;
private TextView adapterTvItemName;
private TextView adapterTvItemQuantity;
private TextView adapterTvItemColor;
private TextView adapterTvItemSize;
private TextView adapterTvItemRemarks;
private CheckBox adapterChkItemChecked;
private ImageButton adapterImgBtnEdit;
private ImageButton adapterImgBtnDelete;
public ViewHolder(@NonNull View itemView) {
super(itemView);
adapterCardItemDisplay = itemView.findViewById(R.id.adapterCardItemDisplay);
adapterTvItemName = itemView.findViewById(R.id.adapterTvItemName);
adapterTvItemQuantity = itemView.findViewById(R.id.adapterTvItemQuantity);
adapterTvItemColor = itemView.findViewById(R.id.adapterTvItemColor);
adapterTvItemSize = itemView.findViewById(R.id.adapterTvItemSize);
adapterTvItemRemarks = itemView.findViewById(R.id.adapterTvItemRemarks);
adapterChkItemChecked = itemView.findViewById(R.id.adapterChkItemChecked);
adapterImgBtnEdit = itemView.findViewById(R.id.adapterImgBtnEdit);
adapterImgBtnDelete = itemView.findViewById(R.id.adapterImgBtnDelete);
}
}
}
Here's a GIF depicting the Problem :
GIF depicting the workings of the view
I hope you can see how the checkBox keeps resetting itself.
Thanks, and best regards.