Search code examples
androidlistviewonclicklistenercustom-adapter

How to update view from an onClickListener inside getView of a CustomAdapter in Android


What do I want to achieve?

  • In the below SS, when user touches 'vote' button, these vertical progress bars (custom) will be set according to the voting percentages retrieved from server for that particular row.

What is the obstacle?

  • I have onClickListener inside getView of the CustomAdapter, and when I manipulate the ProgressBar instance (which is in ViewHolder Class), supposingly I want to see the updated ProgressBar on ONLY the one row of the listview that has triggered that action, but, I see every once 3 rows that I scroll down.
  • Example: I clicked first row, so first row has updated its progress bar, but 4th, 7th, 10th... rows are also updated EVEN IF I don't touch 'vote button'.

My Guessing

  • I think this problem is related to recycling the view, the weird number is 3 in this case but when I make rows smaller it goes '4', so that is the only clue I have.

SS & Codes

ScreenShot: bit.ly/sofscreenshot

Code:

public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;

    LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    if (convertView == null) {
        convertView = mInflater.inflate(layout, null);
        holder = new ViewHolder();

        //some more initialization

        holder.pb1 = (ProgressBar) convertView.findViewById(R.id.leftProgress);
        holder.pb2 = (ProgressBar) convertView.findViewById(R.id.rightProgress);

        holder.leftVoteButton = (Button) convertView.findViewById(R.id.leftButton);
        holder.rightVoteButton = (Button) convertView.findViewById(R.id.rightButton);

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

    holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            holder.pb1.setVisibility(View.VISIBLE);
            holder.pb2.setVisibility(View.VISIBLE);

            /Some codes...

            holder.pb1.setProgress(50);
            holder.pb2.setProgress(50);

        }
    });
}

private class ViewHolder { 
    //some more objects
    ProgressBar pb1;
    ProgressBar pb2;
    Button leftVoteButton;
    Button rightVoteButton; 

}

All the answers and comments are appreciated, have a great day and thank you.


Solution

  • You're doing it wrong.

    The problem is that you need to have a Model somewhere, and change its status. Then the view is updated regarding the model status.

    For example, let's say that this is a "StackOverflow" app, and you have a list of answers. The user upvote the second answer. This means that the second element of the List is upvoted.

    Now what?

    When the adapter is going through your list of object it will "fire" the getView method for that position. Then you have to update that position according to your model. So, if the position is 1, the adapter is trying to show the second Answer, and you have to set the button to "upvoted". Otherwise you have to set it as "normal".

    private List<Answer> answers;
    
    public View getView(int position, View convertView, ViewGroup parent) {
        // here get your view (or initialize it)
    
        // get the matching answer
        Answer answer = answers.get(position);
    
        if(answer.isUpvoted()) {
            holder.pb1.setVisibility(View.VISIBLE);
            holder.pb2.setVisibility(View.VISIBLE);
        } else {
            holder.pb1.setVisibility(View.INVISIBLE);
            holder.pb2.setVisibility(View.INVISIBLE);
        }
    
        holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {
    
            @Override
            public void onClick(View v) {
    
                holder.pb1.setVisibility(View.VISIBLE);
                holder.pb2.setVisibility(View.VISIBLE);
    
                // not sure on how to get this answer here
                // you probably have to go "upper" and manage the click from the ListView
                answer.setUpvoted(true);
            }
        });
    }