Reputation: 73
I've made an RecyclerView which is expandable , my expanded items in RecyclerView have different counts and it's not possible to set a single layout for them.
My program compares the price of some services ( 6 different services for now ) and every service has a different count of sub services which that count will be passed to RV Adapter.
I want somethings like this :
different expanded item counts
I've tried to solve it with these solutions :
FIRST SOLUTION :
my RV data model has a int variable named to serviceCount and gets data from MainActivity for each type of service, my layout should repeat as serviceCount size , I've written this code in onBindViewHolder :
if (holder.detailLayout.getVisibility() == View.VISIBLE) {
for (int i = 0; i < priceList.get(position).getServiceCount(); i++) {
// Code
}
I'm trying to create a layout programmatically and repeat it as that size which is something like this :
for (int i = 0; i < priceList.get(position).getServiceCount(); i++) {
ConstraintLayout newDetailLayout = new ConstraintLayout(context);
ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_PARENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT);
layoutParams.topToBottom = R.id.tv_pricedetail_service;
layoutParams.rightToRight = 0;
layoutParams.leftToLeft = 0;
layoutParams.setMargins(0,margin8dp*i*6,0,0);
Button requestButton = new Button(context);
requestButton.setId(View.generateViewId());
requestButton.setText("درخواست" + " " + String.valueOf(i));
ConstraintLayout.LayoutParams requestButtonParams = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT);
requestButtonParams.leftToLeft = 0;
requestButtonParams.topToTop = 0;
requestButtonParams.setMargins(margin8dp *4,margin8dp *2,0,0);
newDetailLayout.addView(requestButton, requestButtonParams);
TextView serviceName = new TextView(context);
serviceName.setId(View.generateViewId());
serviceName.setText("تست" + " " + String.valueOf(i));
ConstraintLayout.LayoutParams serviceNameParams = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT);
serviceNameParams.topToTop = 0;
serviceNameParams.rightToRight = 0;
serviceNameParams.baselineToBaseline = requestButton.getId();
serviceNameParams.setMargins(0,margin8dp *2,margin8dp *4,0);
newDetailLayout.addView(serviceName, serviceNameParams);
TextView serviceCost = new TextView(context);
serviceCost.setText("هزینه" + " " + String.valueOf(i));
ConstraintLayout.LayoutParams serviceCostParams = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT);
serviceCostParams.leftToRight = requestButton.getId();
serviceCostParams.rightToLeft = serviceName.getId();
serviceCostParams.baselineToBaseline = requestButton.getId();
newDetailLayout.addView(serviceCost, serviceCostParams);
holder.detailLayout.addView(newDetailLayout, layoutParams);
}
//Toast.makeText(context, String.valueOf(priceList.get(position).getServiceCount()), Toast.LENGTH_SHORT).show();
}
output of my code is this : output view BUT when user expand the first item the other items in expanded view copy the first item expanded detail ! and I should create different layout for every expanded layout.
SECOND SOLUTION:
I've made 6 different layout for each service ( they will be more in future ) and inflate them in onCreateViewHolder with instantiated variables
is this right for doing something like this ? or I can do something better ?
EDIT : onBindViewHolder Codes :
public void onBindViewHolder(@NonNull itemsViewHolder holder, int position) {
// Init Layout
final priceItemDM items = priceList.get(position);
holder.iv_logo.setImageDrawable(items.getLogo());
holder.txt_name.setText(items.getName());
holder.txt_price.setText(items.getPrice());
// Expand & Collapse Mode
final boolean isExpanded = position == mExpandedPosition;
final int positionNo = position;
holder.detailLayout.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!items.getPrice().equals(receivingData) && !items.getPrice().equals(receiverError)) {
mExpandedPosition = isExpanded ? -1 : positionNo;
notifyItemChanged(positionNo);
} else {
Toast.makeText(context, "اطلاعات ناقص است ، لطفا مجددا تلاش فرمایید", Toast.LENGTH_SHORT).show();
}
}
});
if (!items.getPrice().equals(receivingData)) {
holder.pb_loading.setVisibility(View.INVISIBLE);
if (holder.detailLayout.getVisibility() == View.VISIBLE &&
!items.getPrice().equals(receiverError)) {
//priceList.get(position).getServiceNames().size()
holder.detailLayout.removeAllViews();
holder.detailLayout.addView(childView);
}
}
}
Upvotes: 1
Views: 710
Reputation: 4573
my expanded items in RecyclerView have different counts and it's not possible to set a single layout for them.
In your case it actually is possible to use a single layout for all items because they all look the same. They all can be expanded in the same way and their contents always look the same - only the amount of child items is different, but it doesn't mean you can't use one layout for them.
You should have two XML layout files - one for the expandable item, and one for the inner child row (the one that has a button).
The first solution is correct, you can't go with the second one. Creating a new layout every time makes no sense because your project will quickly turn into a mess due to the amount of files and the code that inflates them. Although the first solution doesn't have to be that complicated. I see that you are configuring all views in runtime - it would look much simpler if you do it in XML and just inflate the view when needed.
when user expand the first item the other items in expanded view copy the first item expanded detail ! and I should create different layout for every expanded layout.
I'm not sure I get your point but the approach is correct. The only thing is that you have to keep in mind this is a RecyclerView
which reuses its child views when you scroll.
Example: You expand item#1 and inflate 5 child rows in it, then you scroll. If item#4 is also expanded the recycler view will reuse item#1 when showing item#4, i.e. item#4 will automatically get 5 child rows even if it shouldn't have that many.
That means you have to clean up the child rows every time in onBindViewHolder
to make sure you don't display information from the previous item. You will get rid of the problem if your onBindViewHolder
always returns correct representation of a view for the given position. If you forget to clean up some reused views, you might see duplicated information while you scroll. If this is not really clear, please read how the ViewHolder
pattern works, it's pretty simple once you get used to it.
Good luck!
Upvotes: 1