Siddharth
Siddharth

Reputation: 9574

ListView onClick Listeners mixed up

I have items in my listview with a ImageView. When I click the ImageView in the first row, the 4th row item also gets clicked. Why are the onClick getting screwed ? I have new OnClickListener() for each button on each item ?

public View getView(int position, View convertView, ViewGroup parent) {
            final BuddyViewHolder viewHolder;
            if (convertView == null || convertView.getTag() == null) {
                convertView = inflater.inflate(R.layout.buddy, null);
                viewHolder = new BuddyViewHolder();
                allBuddiesViewHolder.add(position, viewHolder) ;

                viewHolder.position = position;
                viewHolder.root = (LinearLayout) convertView
                        .findViewById(R.id.root);
                            viewHolder.request = (ImageView) convertView
                    .findViewById(R.id.request);

                //removed all other viewholder init's
                viewHolder.request.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (selectedBuddy == NO_BUDDY_SELECTED) {
                            viewHolder.request.setVisibility(View.GONE);
                            viewHolder.cancel.setVisibility(View.VISIBLE);
                            viewHolder.root.setSelected(true);
                            selectedBuddy = viewHolder.position;
                        } else {
                            Toast.makeText(context,
                                    "You can make only one request at a time.",
                                    Toast.LENGTH_LONG).show();
                        }
                    }
                });

                convertView.setTag(viewHolder);
            } else
                viewHolder = (BuddyViewHolder) convertView.getTag();

            // here I am setting the values from a arraylist
                    String company = allBuddies.get(position).emailId.substring(
                    allBuddies.get(position).emailId.indexOf("@"),
                    allBuddies.get(position).emailId.indexOf("."));
            viewHolder.name.setText(allBuddies.get(position).name);
            viewHolder.company.setText(company.toLowerCase());
            //removed some lines here.
            return convertView;
        }

ListView XML is

 <ListView
            android:id="@android:id/list"
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_weight="1"
            android:background="@drawable/roundcornersgreyback"
            android:descendantFocusability="blocksDescendants"
            android:divider="#ffffff"
            android:dividerHeight="5dp"
            android:duplicateParentState="true"
            android:scrollbarAlwaysDrawVerticalTrack="true"
            android:scrollbarSize="3dp"
            android:scrollbarStyle="outsideOverlay"
            android:scrollbars="vertical"
            android:scrollingCache="true"
            android:smoothScrollbar="true" />

The item layout (buddy) (only relevant ImageView)

 <ImageView
        android:id="@+id/request"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="3"
        android:adjustViewBounds="true"
        android:background="@drawable/roundcornerblue_button"
        android:enabled="true"
        android:src="@drawable/button_request"
        android:visibility="visible" />

Root Cause

@fasteque

Yes, I guess the problem is related to the "rows reuse" mechanism provided by Android. When convertView is not null, you're returning a view for which you have already set a listener (but it was intended for another row). To be sure about that, disable the viewHolder pattern, just create a new view each time: I guess it will work. Then, if so, move the onClickListener as suggested here above.

Fix

  1. Added these lines after init of convertView (the if/else)
  2. Set allBuddies.get(position).isRequestSent = true in my inline onClick

My Impression

OnItemClickListener way would need a lot of hack. I prefer the inline onClick in this one particular case.

if (allBuddies.get(position).isRequestSent) {
                viewHolder.cancel.setVisibility(View.VISIBLE);
                viewHolder.request.setVisibility(View.GONE);
                viewHolder.confirm.setVisibility(View.GONE);
            } else {
                viewHolder.request.setVisibility(View.VISIBLE);
                viewHolder.cancel.setVisibility(View.GONE);
                viewHolder.confirm.setVisibility(View.GONE);
            }

Upvotes: 0

Views: 337

Answers (1)

fasteque
fasteque

Reputation: 4339

I guess the problem is related to the "rows reuse" mechanism provided by Android. When convertView is not null, you're returning a view for which you have already set a listener (but it was intended for another row).

To be sure about that, try this test: disable the viewHolder pattern, just create a new view each time. I think it will work.

Then, if so, move the onClickListener outside the convertView == null block as suggested by the first comment.

Upvotes: 1

Related Questions