0

I have created a Custom Adapter for a ListView and each element in the listview has 1 TextView and 1 Button

ListView

[TextView 1] [Button 1]

[TextView 2] [Button 2]

..(so on)

My requirement: You are allowed to click button only once. It's like 'Like' button.After the first click, isClickable only for the button in that row should be set to false.

Mycode:

@Override
public View getView(final int position, View convertView, final ViewGroup   parent)  {      
    View row=convertView; 
    ViewHolder holder=null;
    if(row==null)
    {                   
        row=msgInflator.inflate(R.layout.row,parent,false);
        holder = new ViewHolder(row);
        holder.msg=(TextView)row.findViewById(R.id.message_text);
        holder.up=(Button)row.findViewById(R.id.upVote);

        holder.up.setTag(holder);
        holder.up.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                ViewHolder hold=(ViewHolder)v.getTag();
                hold.up.setClickable(false);
            }
        });
        row.setTag(holder);         
    }
    else
    {
        holder=(ViewHolder)row.getTag();            
    }   
    holder.msg.setText(messages.get(position).getMessage());
    holder.up.setText(messages.get(position).getUp());
    return row;
}

Problem:

When i click on Button 1:

Button 1 isClickable set to false -> which is required

Few other Buttons (ex: Button 5,6) isClickable is also set to false -> Which is undesired

How can i achieve my required output?

Ravindra
  • 289
  • 1
  • 4
  • 10

2 Answers2

2

Here is my solution, with two main changes:

1) Maintain a list of button positions that have been disabled. Every time, you click on a button, store its position in the list.

2) Move the onClickListener out of if/else and set it every time. This ensures that despite views being reused, the position is up-to-date.

ArrayList<Integer> positions = new ArrayList<Integer>();

@Override
public View getView(final int position, View convertView, final ViewGroup   parent)  {      
    View row=convertView; 
    ViewHolder holder=null;
    if(row==null)
    {                   
        row=msgInflator.inflate(R.layout.row,parent,false);
        holder = new ViewHolder(row);
        holder.msg=(TextView)row.findViewById(R.id.message_text);
        holder.up=(Button)row.findViewById(R.id.upVote);

        row.setTag(holder);         
    }
    else
    {
        holder=(ViewHolder)row.getTag();            
    }   
    holder.msg.setText(messages.get(position).getMessage());
    holder.up.setText(messages.get(position).getUp());
    holder.up.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            Button btn = (Button)v;
            btn.setClickable(false);
            positions.add(position)
        }
    });
    if(positions.contains(new Integer(position))) {
        holder.up.setClickable(false);
    }
    return row;
}
Amulya Khare
  • 7,718
  • 2
  • 23
  • 38
1

This is due to the behavior of ListView. To have ListView performs as optimized as possible, Android team deploys View reusing technique in ListView intensively.

This mean, the Button you're using in row N, may be reuse again in row N+12. So, you need to update your Button's state everytime getView is being triggered.

Here's what you can do.

    if (off.contains(position)) {
        hold.up.setClickable(false);
    } else {
        hold.up.setClickable(true);
    }

    if (row==null)
    {   
        ...
        holder.up.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // off is Set<Integer>
                off.add(position);
            }
        });
        ....
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875