Im working an app. It fetches lists(country and cities lists) from API using retrofit. On my main activity i have a rv which is the main rv and also displays the country list. my country list items have a sub rv that displays the cities of the country list. Everything is fine so far. but what i wanna do here is to display the list of the cities when i expand the country list item. when i run my app and expand a random country list item and just scroll down i see some of the country list items also expanded while i didnt. what am i doing wrong?
here my codes:
public class CountryRvAdapter extends RecyclerView.Adapter<CountryRvAdapter.ViewHolder> {
private ArrayList<CountryList> countryArrayList;
public CountryRvAdapter(ArrayList<CountryList> countryArrayList) {
this.countryArrayList = countryArrayList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
CountryBinding binding = CountryBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
CountryList countryList = countryArrayList.get(position);
holder.binding.countryName.setText(countryList.getCountry());
CityRvAdapter cityRvAdapter = new CityRvAdapter(countryList.getCitiesList());
holder.binding.cityListRv.setAdapter(cityRvAdapter);
holder.binding.expandCountryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
RecyclerView cityListRv= holder.binding.cityListRv;
if (cityListRv.getVisibility() == View.GONE)
cityListRv.setVisibility(View.VISIBLE);
else
cityListRv.setVisibility(View.GONE);
cityRvAdapter.notifyDataSetChanged();
}
});
}
@Override
public int getItemCount() {
return countryArrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private CountryBinding binding;
public ViewHolder(CountryBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
When using recyclerview, the views that contain the data are recycled. This means that if the country list item in the first view is expanded, it will remain expanded for the next data it will contain. That is why you're seeing that behavior. You have to find a way to save each view's state so that even if the views are recycled, they will display them according to the changes the user has made.
I can think of one way you can fix this. You can give your CountryList
object a new property we can call expanded
. This property is a boolean object that we can get and set. So in your CountryList class, put this:
class CountryList {
// Initial state is false;
Boolean expanded = false;
public Boolean isExpanded(){
return expanded;
}
public void setExpanded(Boolean expanded){
this.expanded = expanded;
}
}
Now, on your adapter, we have to check if the state is expanded. If it's expanded, then let's show the cityListRv
. If it's not, let's hide it. We'll do the checking in onBindViewHolder
method. Also, when the expand button is pressed, don't forget to set the new value for the expanded
property.
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
CountryList countryList = countryArrayList.get(position);
holder.binding.countryName.setText(countryList.getCountry());
// Set adapter for cityListRv
RecyclerView cityListRv = holder.binding.cityListRv;
CityRvAdapter cityRvAdapter = new CityRvAdapter(countryList.getCitiesList());
cityListRv.setAdapter(cityRvAdapter);
// Check if it's expanded or not and update the view
if (countryList.isExpanded()){
// It's expanded so show the cityListRv
cityListRv.setVisibility(View.VISIBLE);
} else {
// It's not expanded so hide the cityListRv
cityListRv.setVisibility(View.GONE);
}
holder.binding.expandCountryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (cityListRv.getVisibility() == View.GONE) {
cityListRv.setVisibility(View.VISIBLE);
countryList.setExpanded(true);
} else {
cityListRv.setVisibility(View.GONE);
countryList.setExpanded(false);
}
cityRvAdapter.notifyDataSetChanged();
}
});
}
This should be working now.