i_me_mine
i_me_mine

Reputation: 1433

Android: Only the last row of my ListView is reacting to an OnClickListener

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

Answers (2)

Bhargav
Bhargav

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

fweigl
fweigl

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

Related Questions