Reputation: 1433
I have a ListView
with a View Holder. When I press a button in one of the rows of my ListView
it's supposed to expand the row at that position and collapse when I press it again. I do this using show/hide on the layouts. However the click is not expanding the correct rows.
Example:
I was clicked
I am a row that was not clicked
I am a row that was not clicked
I am a row that was not clicked // yet my layout was shown and hidden
This happens anytime I scroll, only the last row that's visible in my ListView
will react to any row I press.
Code
public class PetAdapter extends BaseAdapter {
...
...
@Override
public int getCount() {
return mPetList.size();
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
if (mPetList.get(position).getPetType().equals(ConstantValues.CAT)) {
return 0;
} else {
return 1;
}
}
@Override
public Object getItem(int position) {
return mPetList.get(position);
}
@Override
public long getItemId(int position) {
return Integer.parseInt(mPetList.get(position).getId());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Pet pet;
View view = convertView;
switch (getItemViewType(position)) {
case 0:
pet = mPetList.get(position);
mRowType = ConstantValues.CAT;
view = createCatRow(pet, convertView, parent);
break;
case 1:
pet = mPetList.get(position);
mRowType = ConstantValues.DOG;
view = createDogRow(pet, convertView, parent);
break;
}
return view;
}
Inflate Row
public View createCatRow(Pet pet, View convertView, ViewGroup parent) {
if (convertView != null) {
mCatHolder = (CatViewHolder) convertView.getTag();
} else {
convertView = LayoutInflater.from(mContext).inflate(R.layout.cat_row, parent, false);
mCatHolder= new CatViewHolder(convertView);
convertView.setTag(mCatHolder);
}
setCatText(pet);
setCatListeners(pet);
return convertView;
}
Set Text
public void setCatText(Pet pet) {
mCatHolder.catName.setText(pet.getName());
}
Set Listener
public void setCatListeners(Pet pet) {
mCatHolder.catName.setOnClickListener(getCatNameListener(pet));
}
public View.OnClickListener getCatNameListener(final Pet pet) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
expandCard(pet);
}
};
}
SetVisibility
public void expandCard(Pet pet) {
if (mCatHolder.expanded.getVisibility() == View.GONE) {
mCatHolder.collapsed.setVisibility(View.GONE);
mCatHolder.expanded.setVisibility(View.VISIBLE);
} else {
mCatHolder.collapsed.setVisibility(View.VISIBLE);
mCatHolder.expanded.setVisibility(View.GONE);
}
}
View Holder
public static class CatViewHolder {
@Bind(R.id.expanded) RelativeLayout expanded;
@Bind(R.id.collapsed ) RelativeLayout collapsed;
@Bind(R.id.cat_name) TextView catName;
public CatViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
Please help me understand why this behavior is happening. Why does the last row react to OnClick events from the first row. Thank you
Upvotes: 2
Views: 588
Reputation: 8277
You are holding reference to your viewholder in the adapter class, and then applying the onClicklistener to the view. This means the viewholder variable will be referencing the last view rendered, along with setting the onclicklistener to the last view rendered.
You need make the viewholder reference a local variable in your getview method and pass it to all your functions which require the view holder.
Upvotes: 0
Reputation: 22038
In RecyclerView
, views are recycled. So the view from the first row is re-used for later rows and it still has it's OnClickListener
. You have to set the listener in every onBindViewHolder()
.
Upvotes: 2