I am using a custom recyclerview adapter with checkbox so that user can select multiple checked items.
In the beginning I faced duplicate checkbox selection while scrolling down so I added a position array to keep checkbox selection true or false.Now duplicate checkbox selection problem is gone but while scrolling down selected checkbox are deselected.My recyclerview adpater is given below,
class IngredientsAdapter(var context: Context?,var activity: Activity, var ingredientList:ArrayList<String>, var imageID:Int):
RecyclerView.Adapter<IngredientsAdapter.IngredientViewHolder>() {
private var positionArray:ArrayList<Boolean> = ArrayList(ingredientList.size)
private val selectedList=ArrayList<String>()
init {
for (i in ingredientList.indices) {
positionArray.add(false)
}
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): IngredientViewHolder {
val layoutInflater = LayoutInflater.from(p0.context)
return (IngredientsAdapter.IngredientViewHolder(layoutInflater.inflate(R.layout.ingredient_list_row,p0,false)))
}
override fun getItemCount(): Int {
return ingredientList.size
}
override fun onBindViewHolder(p0: IngredientViewHolder, p1: Int) {
p0.imageView.setImageResource(imageID)
p0.textView.text = ingredientList[p1]
p0.checkBox.isChecked = positionArray[p1]
val sharedPreferences= SharedPreferenceHelper(context, SystemConstants.SHARED_PREFS_CHECKDATA)
val checked=sharedPreferences.getPrefsBooleanValue(ingredientList[p1])
p0.checkBox.isChecked = checked
p0.checkBox.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
if(isChecked){
positionArray[p1] = true
selectedList.add(ingredientList[p1])
ingredientList.get(p1)
sharedPreferences.addPrefsBooleanVal(ingredientList[p1],true)
Log.d("debug","selecteditem==>$selectedList")
}else{
positionArray[p1] = false
selectedList.remove(ingredientList[p1])
sharedPreferences.addPrefsBooleanVal(ingredientList[p1],false)
Log.d("debug","selecteditem==>$selectedList")
}
})
}
class IngredientViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textView= itemView.txt_row!!
var imageView= itemView.image_view!!
var checkBox= itemView.chk_row!!
}
}
Any suggestion is appreciated.
OnClickListener won't work when sliding the Switch.
Since RecyclerView is recycling views, a previously attached OnCheckedChangeListener can be triggered when setting checked value for the Switch of the new item.
When binding new data to an item:
switch.setOnCheckedChangeListener(null) // remove any existing listener from recycled view
switch.isChecked = [true/false] // will no longer trigger any callback to listener
switch.setOnCheckedChangeListener { btnView, isChecked ->
// do exiting stuff
}
What #Bek suggested is the correct solution. You will need to use OnClickListener. Also what you commented is not wrong either, just what you used to use(ListView) is or was!
Let me break it down for you:
ListView: The older version of RecyclerView.
Wonder why the developer created another redundant component? Basically Memory Issue! ListView creates as many ItemViews as it requires whereas RecyclerView just recycles the itemView.
For example if for a list of 100 items if we use ListView it will initially create as many ItemViews as the phone can display. Then as we scroll down it keeps creating more Views, till the 100th item(100 ItemViews in memory). But RecyclerView does this more efficiently it creates as many views it can show + 1 (Next view in list) then it keeps recycling them so we always have only As many views in screen +1 and not 100 when we reach the bottom of the list.
For more details read this and this.
OnCheckChangeListener: The Other problem maker! This listener is called whenever check changes for the checkbox, Whenever! So if i where to refresh a checkbox this listener(theoretically) will be called! Getting where am going with this? Yup when used along with RecyclerView its gonna cause an havoc in ones code. The moment recyclerView destroys or reuses an ItemView the checkbox is reset which fires the listener and causing your SharedPref to rewrite the check! I tested this by adding logs inside the listener and saw it get triggered for outer most views when it got recycled.
This is just my findings, there maybe some way or fix for this but i too would suggest using OnClickListener and write a listener to change the model in main class rather than sharedPref in adapter.
Oh! You can use ViewHolder.SetIsRecyclable(false), this would stop RecyclerView from Recycling the views and create as many views as there are items in list. But i wouldn't suggest this as the UI as well as UX will be compromised as u might find a bit lag while scrolling(Memory Issue)!
Long Story Short use OnClickListener with RecyclerView!
When you call setOnCheckedChangeListener, checkbox has a listener, then recyclerview will multiplex view when you scroll.
and you apply p0.checkBox.isChecked = positionArray[p1] , actually you change last poisiton.
For example , you check the first checkbox, arrray[0] is true, then you scroll the view , the next item may the fourth or fifth will have the same checkbox refrence.
Then you call p0.checkBox.isChecked = positionArray[p1] again, but the listener belong the first item . then the listener will change array[0].
So you should call p0.checkBox.setOnCheckedChangeListener before p0.checkBox.isChecked = positionArray[p1]
You can use Model class to keep track of each recyclerView item's checkbox. Use setTag and getTag is used to keep track of checkbox status.
Make Model
public class Model {
private boolean isSelected;
private String animal;
public String getAnimal() {
return animal;
}
public void setAnimal(String animal) {
this.animal = animal;
}
public boolean getSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
create integer.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="btnplusview">1</integer>
<integer name="btnpluspos">2</integer>
</resources>
Finally adapter looks like this:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class CustomAdapter extends
RecyclerView.Adapter<CustomAdapter.MyViewHolder> {
private LayoutInflater inflater;
public static ArrayList<Model> imageModelArrayList;
private Context ctx;
public CustomAdapter(Context ctx, ArrayList<Model> imageModelArrayList) {
inflater = LayoutInflater.from(ctx);
this.imageModelArrayList = imageModelArrayList;
this.ctx = ctx;
}
#Override
public CustomAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int
viewType) {
View view = inflater.inflate(R.layout.rv_item, parent, false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final CustomAdapter.MyViewHolder holder, int
position) {
holder.checkBox.setText("Checkbox " + position);
holder.checkBox.setChecked(imageModelArrayList.get(position).getSelected());
holder.tvAnimal.setText(imageModelArrayList.get(position).getAnimal());
// holder.checkBox.setTag(R.integer.btnplusview, convertView);
holder.checkBox.setTag(position);
holder.checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer pos = (Integer) holder.checkBox.getTag();
Toast.makeText(ctx, imageModelArrayList.get(pos).getAnimal() + "
clicked!", Toast.LENGTH_SHORT).show();
if (imageModelArrayList.get(pos).getSelected()) {
imageModelArrayList.get(pos).setSelected(false);
} else {
imageModelArrayList.get(pos).setSelected(true);
}
}
});
}
#Override
public int getItemCount() {
return imageModelArrayList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
protected CheckBox checkBox;
private TextView tvAnimal;
public MyViewHolder(View itemView) {
super(itemView);
checkBox = (CheckBox) itemView.findViewById(R.id.cb);
tvAnimal = (TextView) itemView.findViewById(R.id.animal);
}
}
}
Related
if I select one item from list another item is also getting selected.
For example, I selected the first item then the first item's color is changing but along with this 13 other items color's are also changing. I am sharing the code files along with this post. Please review it.
Can someone please help me?
public class ArealistAdapter extends
RecyclerView.Adapter<ArealistAdapter.Pendingholder> {
Context context;
List<Area> pendingModels;
RecycleviewOnitemclick recycleviewOnitemclick;
public ArealistAdapter(Context context, List<Area> pendingModels, RecycleviewOnitemclick recycleviewOnitemclick) {
this.context = context;
this.pendingModels = pendingModels;
this.recycleviewOnitemclick = recycleviewOnitemclick;
}
class Pendingholder extends RecyclerView.ViewHolder {
TextView textView4;
public Pendingholder(#NonNull View itemView) {
super(itemView);
textView4=itemView.findViewById(R.id.cbCheck);
}
}
#NonNull
#Override
public ArealistAdapter.Pendingholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(context).inflate(R.layout.search,parent,false);
return new Pendingholder(view);
}
#Override
public void onBindViewHolder(#NonNull final ArealistAdapter.Pendingholder holder, int position) {
Area pendingModel=pendingModels.get(position);
holder.textView4.setText(pendingModel.getArea());
holder.textView4.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.textView4.setTextColor(Color.GREEN);
}
});
}
#Override
public int getItemCount() {
return pendingModels.size();
}
public interface RecycleviewOnitemclick{
void clickitem();
}
}
You need to store selected positions state in a data structure (selectedMap) like Map or SpareArray, and in onBindViewHolder function, just simply check whether item was selected or not:
private Map<Int,Boolean> selectedMap = new HashMap();
#Override
public void onBindViewHolder(#NonNull final ArealistAdapter.Pendingholder holder, int position) {
boolean isPositionSelected = selectedMap.get(position);
if(isPositionSelected ==null || isPositionSelected == false){
holder.textView4.setTextColor(defaultColor);
}else{
holder.textView4.setTextColor(Color.GREEN);
}
}
And inside onClick function, just update the selected state of that position:
holder.textView4.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean isPositionSelected = selectedMap.get(getAdapterPosition());
if(isPositionSelected ==null || isPositionSelected == false){
selectedMap.set(getAdapterPosition(),true);
}else{
selectedMap.set(getAdapterPosition(),false);
}
}
notifyItemChanged(getAdapterPosition());
});
This is happening because recyclerview works on the basic logic of recycling the views. so if you set colour of a view at position 1 as blue but others are black, while recycling it can cause some issues.
Solution:
I personally add a is_selected boolean field in the model class of original array and set it as false by default. when it is selected, you can set the color of view as you want programmatically.
So, in your case:
Add is_selected in Area class.
in onBindViewHolder() class, if you get the is_selected as current position as true, you can set it as selected color or else the default color
in onClick of textview, set the is_selected as true/false for current position.
First of all, try not to put any thing other than setting text or so in your onBindViewHolder as this method is call repeatedly.
To implement a click listener, I would suggest adding it to the view holder class (PendingHolder) just after findViewById as this is called less often.
You can create a method such as :
Private void onClickTxt(Textview tv) {
tv.setOnClickListener..........
}
This method should be called from your view holder class. Not the onBindView.
Feel free to add a comment.
Happy coding.
PS if my answer helped you, a ☑ would be nice
You need to check if the text color is already green or not. If its not green only then change its color, Otherwise keep it as it is. It means you need to add an else condition with the if.
I want to add an onClickListener to items in my RecyclerView. I added the listener in the Holder class as follows:
public class Holder extends RecyclerView.ViewHolder {
TextView firstName;
TextView lastName;
public Holder (final View itemView) {
super(itemView);
firstName = itemView.findViewById(R.id.firstName );
lastName= itemView.findViewById(R.id.lastName);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Do work
}
}
}
But, I think this will cause the scrolling of the list to be a little jerky and not perfectly smooth specially on old devices.
Question 1:
Is there a better way to do that? Or how can I optimize my code?
Question 2:
I intend to add a dynamically changing variable for each item in the list such as a timer, and I don't want the scrolling to be too slow! How should I update the timers the best way?
Create a member variable for item OnClickListener and set it in Holder's constructor.It will be one listener in your adapter when app is running.
Jerky Scrolling
Since you are using RecyclerView I don't think that you will face any issue with scrolling because RecyclerView inherently comes with ViewHolder Pattern. (In case of Simple listView you have to make ViewHolder to avoid jerky scrolling)
Code improvement
Instead of adding a Listener in ViewHolder, make it a Class variable in your RecyclerView Adapter.
There is a standard way to add a Listener in RecyclerView
Create a listener
interface ClickListener{
void click();
}
implement this listener to Your Activity
YourActivity implements ClickListener{
}
Typecast this listener in Your Adapter
YourAdapter extends RecyclerView.Adapter<YourAdapter.Holder>{
ClickListener listener;
public YourAdapter(Context context)
{
this.context = context;
listener = (ClickListener)context;
}
public class Holder extends RecyclerView.ViewHolder {
TextView firstName;
TextView lastName;
public Holder (final View itemView) {
super(itemView);
firstName = itemView.findViewById(R.id.firstName );
lastName= itemView.findViewById(R.id.lastName);
}
// Item Click listener goes here.
#Override
public void onBindViewHolder(DownLoadViewHolder holder, final int position) {
// Do something
listener.click();
}
}
Just giving you the overview.
You can see THIS for reference.
I have a recyclerview which its items contain textView and switchCompat. And in the same activity I have also a textView that have a numerical value in it. The task is when the switchCompat turned on the text view above the recyclerview which contain the numerical value should increase by the value in the recyclerview item textview. I already did that but when scrolling in the recyclerview the switchCompat back to the default state and the value of the numerical textview backs to its old value,
Any help with that?
I Apology for not being able to post a part of the code now and I'll do this as soon as i can, I just posted it now in case anyone pass through something like this before
Thank you
The key to a recycler view or any adapter view in Android is to have the adapter adapt your models to the view. In your case your view is a TextView plus a Switch, so your adapter must adapt some model to this view. In this case I'd choose a simple model like this:
class ItemModel {
String text;
boolean on;
}
I've omitted getters and setters for simplicity
This model contains an string text which reflects the text in your text view and a boolean on that reflects the state of the switch. When true the switch is checked and when false it's unchecked.
There's tons of ways to represent this model. I've chosen this one, you may choose a different one. The point is, you need to save the state somewhere and this is what I mean by model - the view model.
Now let's build an adapter that can do 2 things - Update the models when the switch is clicked and tell the activity that the switch changed state. Here's one way to do this:
public class ItemsAdapter extends
RecyclerView.Adapter<ItemsAdapter.ViewHolder> {
#NonNull
private final List<ItemModel> itemModels;
#Nullable
private OnItemCheckedChangeListener onItemCheckedChangeListener;
ItemsAdapter(#NonNull List<ItemModel> itemModels) {
this.itemModels = itemModels;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false));
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
ItemModel item = itemModels.get(position);
holder.text.setText(item.text);
holder.switchCompat.setChecked(item.on);
// Make sure we update the model if the user taps the switch
holder.switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int adapterPosition = holder.getAdapterPosition();
ItemModel tapped = itemModels.get(adapterPosition);
itemModels.set(adapterPosition, new ItemModel(tapped.text, isChecked));
if (onItemCheckedChangeListener != null) {
onItemCheckedChangeListener.onItemCheckedChanged(adapterPosition, isChecked);
}
}
});
}
#Override
public void onViewRecycled(#NonNull ViewHolder holder) {
super.onViewRecycled(holder);
holder.switchCompat.setOnCheckedChangeListener(null);
}
#Override
public int getItemCount() {
return itemModels.size();
}
public void setOnItemCheckedChangeListener(#Nullable OnItemCheckedChangeListener onItemCheckedChangeListener) {
this.onItemCheckedChangeListener = onItemCheckedChangeListener;
}
interface OnItemCheckedChangeListener {
/**
* Fired when the item check state is changed
*/
void onItemCheckedChanged(int position, boolean isChecked);
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView text;
SwitchCompat switchCompat;
ViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.item_text);
switchCompat = itemView.findViewById(R.id.item_switch);
}
}
}
There's a lot to digest, but let's focus on the important bits - the method onBindViewHolder. The first 3 lines are the classic recycling of the view. We grab the model at the correct position and set the elements in the view that correspond to model's attributes.
Then it gets more interesting. We set a OnCheckedChangeListener to update the model and the activity every time the switch changes state. The first 3 lines change the model in the adapter and the rest uses the custom interface OnItemCheckedChangeListener to notify the listener about the switch change. It's important to notice that inside the method OnCheckedChangeListener you should no longer use position, but rather use holder.getAdapterPosition. This will give you the correct position in the adapter's data list.
Since now the adapter has always the correct models inside the data list, every time the method onBindViewHolder is called the adapter knows exactly how to setup the view. This means that while scrolling and recycling the views, it will preserve the state of each item within the models inside the data list.
It's important to remove the OnCheckedChangeListener when the view gets recycled - onViewRecycled. This avoids messing the count when the adapter is setting the value of switchCompat in the onBindViewHolder.
Here's an example of how the activity could look like:
public class MainActivity extends AppCompatActivity {
private int count = 0;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<ItemModel> data = new ArrayList<>();
for (int i = 1; i <= 100; i++)
data.add(new ItemModel("Item " + i, false));
ItemsAdapter adapter = new ItemsAdapter(data);
((RecyclerView) findViewById(R.id.recyclerview)).setAdapter(adapter);
final TextView countTextView = findViewById(R.id.count);
drawCount(countTextView);
adapter.setOnItemCheckedChangeListener(new ItemsAdapter.OnItemCheckedChangeListener() {
#Override
public void onItemCheckedChanged(int position, boolean isChecked) {
if (isChecked)
count++;
else
count--;
drawCount(countTextView);
}
});
}
private void drawCount(TextView countTextView) {
countTextView.setText(String.valueOf(count));
}
}
This code is meant to demonstrate the idea, not to follow :) In any case, we setup all the initial state and then set up the custom listener OnItemCheckedChangeListener to update the text view in the activity.
The layout files shouldn't be relevant here, but as you can imagine the activity has a text view with id count and there's a recycler view with the id recyclerview.
Hope this helps
It solved for me after adding the below method to the adapter:
#Override
public int getItemViewType(int position) {
return position;
}
I have implement a RecyclerView and it works fine. I have an ArrayList which contains the data for the recycler view. The layout of each item is complicated. It contains two frameLayout. The framelayout1 contains an image and a text and the framelayout2 contains an image and four texts. When the user clicks on the framelayout1 I want to open the Activity1 and when the users clicks on the framelayout2 I want to open the Activity2. I have already search for the onClick in Recycler View and I have found very useful this. But how can I get the position of the arrayList in order to pass it via Intent in the activity1 or activity2?
Try getAdapterPosition() from inside the view holder so that you may get the adapter position of the click the user made.
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(context, String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
}
}
For more in getAdapterPosition() follow this link
Try this
public class ClosetListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ClosetListAdapter (CallBack callback){
this.callback = callback
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder baseholder, int position) {
ViewHolder holder = (ViewHolder) baseholder;
holder.setPosition(position);
holder.name.setText(product.getName());
}
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView name = null;
private OnProductClickListener onProductClickListener;
public ViewHolder(View itemView, OnProductClickListener onClickListener) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.item_name);
itemView.setOnClickListener(this)
}
public void setProdcut(Product product) {
this.product = product;
}
#Override
public void onClick(View v) {
if (callback!= null) {
callback.itemClicked(pos);
}
}
public void setPosition(int position){
this.pos = position;
}
}
interface CallBack {
void itemClicked(int position);
}
}
I've also faced the same problem.
I wanted to find of the position of the clicked/selected item of the RecyclerView() and perform some specific operations on that particular item.
getAdapterPosition() method works like a charm for these kind of stuff. I found this method after a day of long research and after trying numerous other methods.
int position = getAdapterPosition();
Toast.makeText(this, "Position is: "+position, Toast.LENGTH_SHORT).show();
You do not have to use any extra method. Just create a global variable named 'position' and initialize it with getAdapterPosition() in any of the major method of the adapter (class or similar).
Here is a brief documentation from this link.
getAdapterPosition
added in version 22.1.0
int getAdapterPosition ()
Returns the Adapter position of the item represented by this ViewHolder.
Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet.
RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.
Happy to help. Feel free to ask doubts.
in my app, I'm implementing a recyclerview. My dataset for this recyclerview will have varying sizes according to the options that I set for the data to be displayed on the recyclerview.
One of the actions that I take with my recyclerview is to "expand" an item when a click is done on it, displaying further options in it. When pressing on this "expanded" item, I perform the action of "closing" it. Also, there can only be on "expanded" item at maximum at any moment.
The thing is that I understand that recyclerview recycles its row-views when they get out of sight for improved performance. However, because I am trying to have only one "expanded" item at a time, this recycling messes it up quite a lot.
What happens right now is that when I "expand", say the item related to position 1 of my dataset, as shown in the image below.
When I scroll down, I will see that the rowview for this item being recycled at a random chance since I will see this "expanded" view on items that I have not set to be "expanded", as shown in the image below.
And of course, when this happens, then when I scroll back to the item that I have selected to "expand", it will be "closed" as you would have expected.
So I have been thinking that I could resolve this problem by setting the possible number of views to be something like 80% of my dataset size will decrease the possibility of this problem occurring while still reduced, but enjoy some improved performance.
Another solution I thought about was disabling this "expanded" view from being recycled for other views and when this "expanded" item's position comes into screen, it gets bounded to this specific view. I thought of this solution after seeing that there is a concept of "scrap" and "recycle" for recyclerview, but I am not so sure if this method is even possible because I think I have only vaguely understood this side of recyclerview.
That being said, my question is are there ways for me to set the number of views to be recycled for a recycled view? Or even better, having one view from being recycled for items other than the "expanded" item?
Thanks in advance.
EDIT:
here's my (I know it's very messy I'm sorry...) code for my adapter:
public class DrinkMenuItem extends RecyclerView.Adapter<DrinkMenuItem.ViewHolder> {
private Context context;
private ViewGroup parent;
private ArrayList<Drink> menu;
private ArrayList<DrinkSelected> selected;
private DrinkMenuBasketItem selectedAdapter;
public int expanded = -1;
public boolean expandedVisible = false;
private DrinkMenuDropdownItem dropdownAdapter;
public static class ViewHolder extends RecyclerView.ViewHolder {
public RelativeLayout layout;
public TextView name, price;
public ListView dropdown;
public RelativeLayout basket;
public boolean tabbed = false;
public ViewHolder(View itemView) {
super(itemView);
layout = (RelativeLayout)itemView.findViewById(R.id.drink_menu_layout);
name = (TextView)itemView.findViewById(R.id.drink_menu_name);
price = (TextView)itemView.findViewById(R.id.drink_menu_price);
dropdown = (ListView)itemView.findViewById(R.id.drink_menu_dropdown_list);
basket = (RelativeLayout)itemView.findViewById(R.id.drink_menu_basket_button);
}
}
public DrinkMenuItem(Context context, ArrayList<Drink> menu, ArrayList<DrinkSelected> selected, DrinkMenuBasketItem selectedAdapter) {
this.context = context;
this.menu = menu;
this.selected = selected;
this.selectedAdapter = selectedAdapter;
this.dropdownAdapter = null;
}
public void updateDropdown(int requestedOption, int responsedOptionitem) {
dropdownAdapter.updateSelectedOption(requestedOption, responsedOptionitem);
notifyDataSetChanged();
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
this.parent = parent;
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_drink_menu, parent, false);
return new ViewHolder(itemView);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Drink d = menu.get(position);
holder.name.setText(d.getName());
holder.price.setText(d.getPrice() + d.totalAdditionalPrice() + "원");
if(position == expanded) {
//delete dropdown
holder.dropdown.setAdapter(null);
menu.get(position).returnToUnselected();
holder.price.setText(menu.get(position).getPrice() + "원");
setListViewHeight(holder.dropdown);
//reset dropdown-related stuff
holder.tabbed = false;
holder.basket.setVisibility(View.GONE);
}
setOnClickEvent(holder, position, parent);
}
private void setOnClickEvent(final ViewHolder holder, final int position, final ViewGroup parent) {
holder.layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!holder.tabbed) {
//close dropdown of expanded view
if(expanded != -1) notifyItemChanged(expanded);
//make dropdown
dropdownAdapter = new DrinkMenuDropdownItem(context, menu, position, holder.price);
holder.dropdown.setAdapter(dropdownAdapter);
setListViewHeight(holder.dropdown);
//set dropdown-related stuff
holder.tabbed = true;
holder.basket.setVisibility(View.VISIBLE);
expanded = position;
expandedVisible = true;
((RecyclerView) parent).smoothScrollToPosition(position);
} else {
//delete dropdown
holder.dropdown.setAdapter(null);
menu.get(position).returnToUnselected();
holder.price.setText(menu.get(position).getPrice() + "원");
setListViewHeight(holder.dropdown);
expanded = -1;
expandedVisible = false;
//reset dropdown-related stuff
holder.tabbed = false;
holder.basket.setVisibility(View.GONE);
}
}
});
...
}
}
I have exactly same issue in my project. I did not succeed solving it with recyclerView. But the solution would be one of the following:
Create an expandableListView instead of recyclerView and everything will work great.
Create a ScrollView, and put a LinearLayout with android:orientation="vertical". Then, create a loop and insert all your custom views, and set click listener where you wish to expand.
Use an Expandable RecyclerView Library like one of these:
https://github.com/h6ah4i/android-advancedrecyclerview
https://github.com/bignerdranch/expandable-recycler-view