0

My problem is as follows:
I want to change recyclerView card size dynamically also with the margins between textViews inside.
Three cases:
1. Amount is empty so two textViews inside recyclerView card need to be slightly squeezed.
I do it like this:

@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        holder1.amount.setText(String.valueOf(currentItem.getAmount()));
        ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.clone(constraintLayout_A);
        constraintSet.connect(R.id.amount,ConstraintSet.START, ConstraintSet.PARENT_ID,ConstraintSet.START,70);
        constraintSet.connect(R.id.type, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END,70);
        constraintSet.applyTo(constraintLayout_A);
    }

2. Amount is very big so two textViews will be shifted into two lines.
I do it like this:

@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        holder2.amount.setText(String.valueOf(currentItem.getAmount()));
        ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.clone(constraintLayout_A);
        constraintSet.connect(R.id.amount,ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP,0);
        constraintSet.connect(R.id.amount,ConstraintSet.START, ConstraintSet.PARENT_ID,ConstraintSet.START,0);
        constraintSet.connect(R.id.amount,ConstraintSet.END, ConstraintSet.PARENT_ID,ConstraintSet.END,0);
        constraintSet.connect(R.id.amount,ConstraintSet.BOTTOM, R.id.type,ConstraintSet.TOP,0);
        constraintSet.connect(R.id.type, ConstraintSet.BOTTOM, R.id.info, ConstraintSet.TOP, 0);
        constraintSet.connect(R.id.type, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START,0);
        constraintSet.connect(R.id.type, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END,0);
        constraintSet.connect(R.id.type, ConstraintSet.TOP, R.id.amount, ConstraintSet.BOTTOM,0);
        constraintSet.connect(R.id.info, ConstraintSet.START,ConstraintSet.PARENT_ID,ConstraintSet.START,0);
        constraintSet.connect(R.id.info, ConstraintSet.END,ConstraintSet.PARENT_ID,ConstraintSet.END,0);
        constraintSet.connect(R.id.info, ConstraintSet.TOP, R.id.type,ConstraintSet.BOTTOM,5);
        constraintSet.applyTo(constraintLayout_A);
    }

3. Amount is "normal" it takes about 2-3 'places'.
Here I leave the code unchanged.

@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        holder3.amount.setText(String.valueOf(currentItem.getAmount()));
    }

The problem is:
When I scroll the recyclerView up and down , it happens that lines that don't need to be changed they are.
For example amount is '0' and the text appears in two lines.
It comes from that recylerView recovers the Views (that's how I understand it).
Only holder.setIsRecyclable(false); set up in the adapter solves the problem but it is not efficient solution.
I tried to break it down into three separate Holders.
But the problem is still the same, maybe I'm doing something wrong?
I thought that it will solve the problem.
So is this the right approach or I have to do it yet otherwise?

EDIT

package com.example.appName.Adapters;

public class Recycler_Adapter_2 extends ListAdapter<Item_get, RecyclerView.ViewHolder> implements Filterable {

   public Recycler_Adapters_items clickListener;
   private Item_get currentItem;
   private ConstraintLayout constraintLayout_A;
   private Context context;
   private RecHolder holder1BASIC;
   private RecHolder2 holder2EXTENDED;
   private RecHolder4 holder4EXTENDED;
   public Recycler_Adapter_2(String className, RecyclerView recyclerView) {
      super(DIFF_CALLBACK);
      this.className = className;
      ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(recyclerView.getLayoutParams());
      params.setMarginEnd(5);
      context = recyclerView.getContext();
   }
   private static final DiffUtil.ItemCallback<Item_get> DIFF_CALLBACK = new DiffUtil.ItemCallback<Item_get>() {
      @Override
      public boolean areItemsTheSame(@NonNull Item_get oldItem, @NonNull Item_get newItem) {
         return oldItem.getId() == newItem.getId();
      }

      @Override
      public boolean areContentsTheSame(@NonNull Item_get oldItem, @NonNull Item_get newItem) {
         if(oldItem.getBuy_place() != null){
            return oldItem.getname_product().equals(newItem.getname_product()) &&
                    oldItem.getAmount().equals(newItem.getAmount()) && oldItem.getBuy_place().equals(newItem.getBuy_place());
         }else {
            return oldItem.getname_product().equals(newItem.getname_product()) &&
                    oldItem.getAmount().equals(newItem.getAmount());
         }

      }
   };
   public void setOnItemClickListener(Recycler_Adapters_items clickListener){
      this.clickListener = clickListener;
   }

   class RecHolder extends RecyclerView.ViewHolder {
      public TextView name_product;
      public TextView amountBuy;
      public TextView typePackage;

      public RecHolder(@NonNull final View itemView, final Recycler_Adapters_items listener) {
         super(itemView);
         name_product = itemView.findViewById(R.id.name_field);
         amountBuy = itemView.findViewById(R.id.amountBuy);
         typePackage = itemView.findViewById(R.id.typeP);
         constraintLayout_A = itemView.findViewById(R.id.ConstrainLayoutShop1_A);
         initAllNeededArrays();
         itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               if(listener != null){
                  int position = getAbsoluteAdapterPosition();
                  if(position != RecyclerView.NO_POSITION){
                     listener.onItemClick(getItem(position), getAbsoluteAdapterPosition());
                  }
               }
            }
         });
      }
   }

   class RecHolder2 extends RecyclerView.ViewHolder {
      public TextView name_product;
      public TextView amountBuy;
      public TextView typePackage;

      public RecHolder2(@NonNull final View itemView, final Recycler_Adapters_items listener) {
         super(itemView);
         name_product = itemView.findViewById(R.id.name_field);
         amountBuy = itemView.findViewById(R.id.amountBuy);
         typePackage = itemView.findViewById(R.id.typeP);
         constraintLayout_A = itemView.findViewById(R.id.ConstrainLayoutShop1_A);
         initAllNeededArrays();
         itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               if(listener != null){
                  int position = getAbsoluteAdapterPosition();
                  if(position != RecyclerView.NO_POSITION){
                     listener.onItemClick(getItem(position), getAbsoluteAdapterPosition());
                  }
               }
            }
         });
      }
   }
   class RecHolder4 extends RecyclerView.ViewHolder {
      public TextView name_product;
      public TextView amountBuy;
      public TextView typePackage;

      public RecHolder4(@NonNull final View itemView, final Recycler_Adapters_items listener) {
         super(itemView);
         name_product = itemView.findViewById(R.id.name_field);
         amountBuy = itemView.findViewById(R.id.amountBuy);
         typePackage = itemView.findViewById(R.id.typeP);
         constraintLayout_A = itemView.findViewById(R.id.ConstrainLayoutShop1_A);
         initAllNeededArrays();
         itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               if(listener != null){
                  int position = getAbsoluteAdapterPosition();
                  if(position != RecyclerView.NO_POSITION){
                     listener.onItemClick(getItem(position), getAbsoluteAdapterPosition());
                  }
               }
            }
         });
      }
   }
   @NonNull
   @Override
   public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
      View v = null;
      if(className.equals(ShoppList.class.getName())) {
         v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_template2,parent,false);
         switch (viewType){
            case 0:
               return new RecHolder(v,clickListener);
            case 2:
               return new RecHolder2(v,clickListener);
            case 4:
               return new RecHolder4(v,clickListener);
         }
      }
      return new RecHolder(v,clickListener);
   }

   @Override
   public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
      switch (holder.getItemViewType()) {
         case 0:
            holder1BASIC = (RecHolder) holder;
            if (className.equals(ShoppList.class.getName())) {
               currentItem = getItem(position);
               holder1BASIC.name_product.setText(currentItem.getname_product());
               holder1BASIC.amountToBuy.setText(String.valueOf(currentItem.getAmount()));
               setPackageText0(holder1BASIC, currentItem.getType(), currentItem.getAmount());
            }
            break;
         case 2:
            holder2EXTENDED = (RecHolder2) holder;
            if (className.equals(ShoppList.class.getName())) {
               currentItem = getItem(position);
               holder2EXTENDED.name_product.setText(currentItem.getname_product());
               holder2EXTENDED.amountToBuy.setText(String.valueOf(currentItem.getAmount()));
               setPackageText2(holder2EXTENDED, currentItem.getType(), currentItem.getAmount());

            }
            break;
         case 4:
            holder4EXTENDED = (RecHolder4) holder;
            if (className.equals(ShoppList.class.getName())) {
               currentItem = getItem(position);
               holder4EXTENDED.name_product.setText(currentItem.getname_product());
               holder4EXTENDED.amountToBuy.setText(String.valueOf(currentItem.getAmount()));
               setPackageText4(holder4EXTENDED, currentItem.getType(), currentItem.getAmount());
            }
      }
   }


   public void nextLineText(){
      ConstraintSet constraintSet = new ConstraintSet();
      constraintSet.clone(constraintLayout_A);
      constraintSet.connect(R.id.amount,ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP,0);
      constraintSet.connect(R.id.amount,ConstraintSet.START, ConstraintSet.PARENT_ID,ConstraintSet.START,0);
      constraintSet.connect(R.id.amount,ConstraintSet.END, ConstraintSet.PARENT_ID,ConstraintSet.END,0);
      constraintSet.connect(R.id.amount,ConstraintSet.BOTTOM, R.id.type,ConstraintSet.TOP,0);
      constraintSet.connect(R.id.type, ConstraintSet.BOTTOM, R.id.info, ConstraintSet.TOP, 0);
      constraintSet.connect(R.id.type, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START,0);
      constraintSet.connect(R.id.type, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END,0);
      constraintSet.connect(R.id.type, ConstraintSet.TOP, R.id.amount, ConstraintSet.BOTTOM,0);
      constraintSet.connect(R.id.info, ConstraintSet.START,ConstraintSet.PARENT_ID,ConstraintSet.START,0);
      constraintSet.connect(R.id.info, ConstraintSet.END,ConstraintSet.PARENT_ID,ConstraintSet.END,0);
      constraintSet.connect(R.id.info, ConstraintSet.TOP, R.id.type,ConstraintSet.BOTTOM,5);
      constraintSet.applyTo(constraintLayout_A);
   }

   public void setPackageText0( RecHolder holder, int packageTypeCode, int amount){
      if(packageTypeCode == 1){
         if(amount == 0){
            holder.typePackage.setText(packTypeTextResources[2]);
         }else if(amount == 1){
            holder.typePackage.setText(packTypeTextResources[0]);
         }else if(amount >= 2 && amount <= 4){
            holder.typePackage.setText(packTypeTextResources[1]);
         }else if(amount >= 5){
            holder.typePackage.setText(packTypeTextResources[2]);
         }
      }
   }

   public void setPackageText2( RecHolder2 holder2, int packageTypeCode, int amount){
      if(packageTypeCode == 1){
         if(amount == 0){
            holder2.typePackage.setText(packTypeTextResources[2]);
            changeMarginDistance();
         }else if(amount == 1){
            holder2.typePackage.setText(packTypeTextResources[0]);
            changeMarginDistance();
         }else if(amount >= 2 && amount <= 4){
            holder2.typePackage.setText(packTypeTextResources[1]);
            changeMarginDistance();
         }else if(amount >= 5){
            holder2.typePackage.setText(packTypeTextResources[2]);
            changeMarginDistance();
         }
      }else {
         holder2.typePackage.setText(packTypeTextResources[12]);
         changeMarginDistance();
      }
   }

   public void setPackageText4( RecHolder4 holder4, int packageTypeCode, int amount){
      nextLineText();
      if(packageTypeCode == 1){
         if(amount == 0){
            holder4.typePackage.setText(packTypeTextResources[2]);
         }else if(amount == 1){
            holder4.typePackage.setText(packTypeTextResources[0]);
         }else if(amount >= 2 && amount <= 4){
            holder4.typePackage.setText(packTypeTextResources[1]);
         }else if(amount >= 5){
            holder4.typePackage.setText(packTypeTextResources[2]);
         }
      }else {
         holder4.typePackage.setText(packTypeTextResources[12]);
      }
   }

   public Item_get getItemPos(int position){
      return getItem(position);
   }

   private void initPackageArrays(){
      packTypeTextResources = context.getResources().getStringArray(R.array.Package_Type_Diffrent_varieties);
   }

   private void initAllNeededArrays(){
      initPackageArrays();
   }

   @Override
   public int getItemViewType(int position) {
      // return super.getItemViewType(position);
      Item_get actualItem = getItem(position);
      if(actualItem.getAmount() == 0 || actualItem.getAmount() == null){
         return 2;
      }else if (actualItem.getAmount() > 999){
         return 4;
      }
      return 0;
   }

   public void changeMarginDistance(){
      ConstraintSet constraintSet = new ConstraintSet();
      constraintSet.clone(constraintLayout_A);
      constraintSet.connect(R.id.amount,ConstraintSet.START, ConstraintSet.PARENT_ID,ConstraintSet.START,70);
      constraintSet.connect(R.id.type, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END,70);
      constraintSet.applyTo(constraintLayout_A);
   }
}

I pasted code from the adapter (in short), to clarify the question.

Rivke
  • 25
  • 5
  • Where are `holder1`, `holder2`, `holder3` defined? Why do you have three instance of `onBindViewHolder` defined? – Tyler V Jul 15 '22 at 04:34
  • I did it like in this post: [link](https://stackoverflow.com/questions/26245139/how-to-create-recyclerview-with-multiple-view-types) In my `getItemViewType` I get the actual amount and based on the length, return 0,2,4. – Rivke Jul 15 '22 at 05:33

1 Answers1

0

TL;DR To fix your problem you should:

  • Use a separate XML layout file for each row type with constraints set in the XML instead of trying to adjust constraints in code
  • Don't store views or view holders as class members in the adapter, use the passed-in holder in onBindViewHolder
  • Define only a single onBindViewHolder and use the holder's row type integer to cast it correctly and set its views in there

Problem

It's not clear where holder1, holder2, holder3, or constrainLayout_A are defined in your example, but the fact that you aren't using the passed in holder instance in onBindViewHolder and apparently have defined onBindViewHolder three times (?) is probably the root cause of the issues you are seeing. It is almost always a bug to store row views as class members directly in a RecyclerView adapter - row views should only be stored in a ViewHolder, and the RecyclerView base class should manage storing and using ViewHolder instances.

Solution

You can use different view types for the different rows in the RecyclerView. To do this, then you don't need to adjust your constraints manually in code. Just set different XML layouts for the different row types and set the constraints in the XML. Then pick the correct XML and holder to use in onCreateViewHolder.

Once you have defined getItemViewType to set the row type based on some criteria you need to use that type integer in both onCreateViewHolder (to inflate the right XML layout and create the right holder) and onBindViewHolder (to cast the holder to the right type and set the right data). For example:

private final int ROW_TYPE_TITLE = 1;
private final int ROW_TYPE_ITEM = 0;

// Step 1: let the RecyclerView know what "type" each row is
@Override
public int getItemViewType(int position) {
    return mData.get(position).isTitle ? ROW_TYPE_TITLE : ROW_TYPE_ITEM;
}
// Step 2: Define different view holders to use for each row type
class TitleViewHolder extends RecyclerView.ViewHolder {
    public TitleViewHolder(View v) {
        super(v);
        title = v.findViewById(R.id.title);
        icon = v.findViewById(R.id.icon);
    }
    
    public TextView title;
    public ImageView icon;
}

class ItemViewHolder extends RecyclerView.ViewHolder {
    public ItemViewHolder(View v) {
        super(v);
        item = v.findViewById(R.id.item);
    }
    
    public TextView item;
}
// Step 3: when creating the view holder, use the provided type to create
// the correct holder. Each holder can have its own XML file defined, so
// you can set constraints appropriately in the XML
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case ROW_TYPE_TITLE: 
            View view = mInflater.inflate(R.layout.list_title, parent, false);
            return new TitleViewHolder(view);
        
        case ROW_TYPE_ITEM: 
            View view = mInflater.inflate(R.layout.list_row, parent, false);
            return new ItemViewHolder(view);
    }
}
// Step 4: When binding a holder, cast the passed in holder to the correct
// type then set its data (in a *single* call to onBindViewHolder). Since you
// set the constraints for each type in its XML, you don't need to adjust them
// here.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holderBase, int position) {
    MyData row_data = mData.get(position);

    switch(holder.getItemViewType())
        case ROW_TYPE_TITLE: 
            TitleViewHolder holderT = (TitleViewHolder)holderBase;
            holderT.title.setText(row_data.title);
            break;
        
        case ROW_TYPE_ITEM: 
            ItemViewHolder holderI = (ItemViewHolder)holderBase;
            holderI.item.setText(row_data.item);
            break;
    }
}
Tyler V
  • 9,694
  • 3
  • 26
  • 52
  • Thanks, for the answer I will check it. Also I passed the code from my adapter in short. Can you look at it and tell me what is wrong? Maybe all code should be thrown in the trash? I remembered why I set up the margins programmatically, because round corner of cards didn't work and remained as it was default. – Rivke Jul 18 '22 at 07:19