简体   繁体   中英

Android: Change Button background in ListView Row with onClick

My rows contain a button that has its own click listener set in my adapter's getView. I'm able to distinguish between my button clicks and the actual row item clicks using android:descendantFocusability="blocksDescendants" in the row's parent.

When I click on a button it sets the button background properly, my problem is as I scroll through the list its setting it for different rows as well. I assume theirs an issue somewhere with views recycling.

Here's my code:

@Override
public View getView(int position, View convertView, ViewGroup parent){

    if(convertView == null){

        holder = new ViewHolder();

        convertView = inflater.inflate(R.layout.todays_sales_favorite_row, null);
        holder.favCatBtn = (Button)convertView.findViewById(R.id.favCatBtn);            

        convertView.setTag(holder);

    } else {
        holder = (ViewHolder)convertView.getTag();
    }

        holder.favCatBtn.setTag(position);
        holder.favCatBtn.setOnClickListener(this);

    return convertView;
 }

@Override
public void onClick(View v) {
    int pos = (Integer) v.getTag();
    Log.d(TAG, "Button row pos click: " + pos);
    RelativeLayout rl = (RelativeLayout)v.getParent();
    holder.favCatBtn = (Button)rl.getChildAt(0);
    holder.favCatBtn.setBackgroundResource(R.drawable.icon_yellow_star_large);

}

So if i click on the button at row position 1 the button background changes as it should. But then as i scroll down the list random other buttons are getting set as well. Then sometimes when I scroll back up to position 1 the button background reverts back to the original again.

What am I missing here? I know I'm right there its just something minor I'm not doing.

Yes, you are right, the views are recycled. You will need to track which positions have been clicked on and update the background resource in your getView method. For example, I extended your code to add background toggling:

private final boolean[] mHighlightedPositions = new boolean[NUM_OF_ITEMS];

@Override
public View getView(int position, View convertView, ViewGroup parent){

    if(convertView == null){
        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.todays_sales_favorite_row, null);
        holder.favCatBtn = (Button)convertView.findViewById(R.id.favCatBtn);
        holder.favCatBtn.setOnClickListener(this);
        convertView.setTag(holder);
    }else {
        holder = (ViewHolder)convertView.getTag();
    }

    holder.favCatBtn.setTag(position);

    if(mHighlightedPositions[position]) {
        holder.favCatBtn.setBackgroundResource(R.drawable.icon_yellow_star_large);
    }else {
        holder.favCatBtn.setBackgroundResource(0);
    }

    return convertView;
}

@Override
public void onClick(View view) {
    int position = (Integer)view.getTag();
    Log.d(TAG, "Button row pos click: " + position);

    // Toggle background resource
    RelativeLayout layout = (RelativeLayout)view.getParent();
    Button button = (Button)layout.getChildAt(0);
    if(mHighlightedPositions[position]) {
        button.setBackgroundResource(0);
        mHighlightedPositions[position] = false;
    }else {
        button.setBackgroundResource(R.drawable.icon_yellow_star_large);
        mHighlightedPositions[position] = true;
    }
}

I found a perfect, short and clean solution for this using StateListDrawable:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    currentPosition = position;
    holder = null;
    if (convertView == null) {
        holder = new Holder();
        LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = vi.inflate(R.layout.grid_item, null);
        holder.imageView = (ImageView) convertView.findViewById(R.id.gridItemBtn);

        StateListDrawable states = new StateListDrawable();
        states.addState(new int[] {android.R.attr.state_pressed},
                ContextCompat.getDrawable(context, R.drawable.pressed_state));
        states.addState(new int[] {android.R.attr.state_focused},
                ContextCompat.getDrawable(context, R.drawable.focused_state));
        states.addState(new int[]{},
                ContextCompat.getDrawable(context, R.drawable.default_state));
        holder.imageView.setImageDrawable(states);
    }

    return convertView;
}

This works still perfect together with OnClickListener where you can do your important stuff.

holder.btnUnLock.setOnClickListener(new OnClickListener() {

  @Override
  public void onClick(View v) {
 // TODO Auto-generated method stub
 // Button btn = Button(v);
   holder = (ViewHolder) v.getTag();
   holder.btnSetLock.setBackgroundResource(R.drawable.btn_lock_bg_right);
holder.btnUnLock.setBackgroundResource(R.drawable.btn_unlock_bg_left);

}
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM