0

I am creating an E-commerce app to display a list of products in a listView. Each list item has a title, image and buttons named as like, dislike and favorite.

When the user presses the like button on a particular list item, the image of the button should change to "liked" and a particular API is called. I have done this inside the onClickListener inside the getView() method using ArrayAdapter class.

Like this.

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

    View listItemView = convertView;
    if (listItemView == null) {
        listItemView = LayoutInflater.from(getContext()).inflate(
                R.layout.list_item, parent, false);
        likeButton= (Button) 
 listItemView.findViewById(R.id.like_button);
        //likeButton.setClickable(currentProduct.getmLikeButton());
        dislikeButton = (Button) 
 listItemView.findViewById(R.id.dislike_button);

        favoritesButton = (Button) 
  listItemView.findViewById(R.id.favorite_button);
    }

    TextView productNameView = (TextView) 
 listItemView.findViewById(R.id.product_name);
    ImageView productImageView = (ImageView) 
  listItemView.findViewById(R.id.product_image);

    currentProduct = getItem(position);
    likeButton.setTag(position);
    dislikeButton.setTag(position);
    favoritesButton.setTag(position);

    likeButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            int position=(Integer)v.getTag();
            currentProduct = getItem(position);
            productId = currentProduct.getProductId();

            likeButton = (Button) v.findViewById(R.id.like_button);

            Uri baseUri = Uri.parse(UNBXD_UACTION);
                Uri.Builder uriBuilder = baseUri.buildUpon();
                uriBuilder.appendQueryParameter("uaction", "like");
                uriBuilder.appendQueryParameter("user", "8222");
                uriBuilder.appendQueryParameter(PRODUCT_ID,productId);
                uriBuilder.appendQueryParameter(MATERIALL, "true");
                //uriBuilder.appendQueryParameter(USER_ID, "5");
                uriBuilder.appendQueryParameter(UNDO, "false");
                Intent intent = new 
                Intent(Intent.ACTION_WEB_SEARCH, uriBuilder.build());
                intent.getData();
                likeButton.setBackgroundResource(R.drawable.liked);
Log.v("liked url is ", uriBuilder.toString() + " " + 
String.valueOf(position));
        }
    });

The problem I noticed is that, if I click the like button on the first item in the listView, it's not just the first item's like button that changes to 'liked' but also the like buttons in every third listView item that are displayed changes to 'liked' but the API call happens only once and correctly appropriate to the item clicked.

I am not able to figure out exactly how and why every third item's like button is getting pressed starting from the like button that I actually pressed but (fortunately) only one and also the correct API is called.

Please help.

ShashankAC
  • 1,016
  • 11
  • 25

1 Answers1

0

That is because your views are recycled in ListView. When convertView != null that means you get a recycled view, and you need to bind the view again.

To fix this issue, you may need to add a new property in your Product class or adapter or somewhere, and simplify the action in your click listener:

likeButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        int position = (Integer)v.getTag();
        Product currentProduct = getItem(position);
        currentProduct.setLiked(true); // or false
        notifyDataSetChanged(); // notify so ListView can update
    }
// continue...

Then in your getView method:

@Override
public View getView(int position, View convertView, ViewGroup parent) 
{
    // ...
    likeButton = (Button) v.findViewById(R.id.like_button);
    likeButton.setBackgroundResource(currentProduct.isLiked() ? R.drawable.liked : R.drawable.something);
    // continue...
Aaron
  • 3,764
  • 2
  • 8
  • 25
  • Thanks for the answer, I still have a problem. If I remove the setBackgroundResource statement from inside the onClick() method, the image doesn't even change after pressing the button, but the API is called. – ShashankAC Nov 04 '18 at 11:08
  • @ShashankAC Sorry, my bad, you have to call `notifyDataSetChanged` after `currentProduct.setLiked(true)` otherwise your `ListView` doesn't know if anything has changed. I've updated the answer. – Aaron Nov 04 '18 at 11:13
  • It's not working. I read that notifyDataSetChanged() works only when you use, add(), insert(), remove() clear(). https://stackoverflow.com/questions/3669325/notifydatasetchanged-example – ShashankAC Nov 04 '18 at 14:42
  • It also works when your data has changed, it even says in the method name. Unless you did not bind the views properly, or set your data correctly. – Aaron Nov 04 '18 at 18:40
  • I don't know what you mean by that, can you tell me where you think it might be wrong ?, I used count variables to see exactly how many times the onClick method is called inside the getView after I click on a like button once. It is called exactly once, although getView() is called every time I scroll up and down the listView. Everything in the onClick method is run only once except the setBackgroundResource statement used to change the image. – ShashankAC Nov 06 '18 at 03:18
  • Right.. `ListView` only renders as many views as it needs to fit onto a screen, it does not always render all the views if they are too tall for the screen. It can call `getView` for a position more than once, depends on where the view is or when it scrolls. When `convertView` is not null, it tells you the view is recycled or dirty (reason why you see multiple button pressed), and you need to bind the view again. – Aaron Nov 06 '18 at 04:05
  • Thanks, now I understood the theory part, now I will have figure out the code for it. – ShashankAC Nov 06 '18 at 12:35