androidandroid-arrayadapterclicklistener

Changing text of a TextView that's inside an ArrayAdapter by pressing a Button within the same adapter


This is my first question here in stackoverflow. Please correct me if I'm doing something wrong. So.. Here we go:

I'm trying to create an onClickListener of a Button inside an ArrayAdapter that changes the text of a TextView within the same layout. But what's happening is: when I click the Button, it changes the text of more than one TextView. It's like this: if I press the first Button of the list, it changes the text of the 5h, 9th, 13th..... Why is thath happening?

I'd like to have an onClickListener that changes the text of the TextView that is located on the samelayout.

PS: When I say "Button" it's actually an "ImageView"

The adapter's code:

public class ProductAdapter extends ArrayAdapter<Produtc> {

public ProductAdapter (Context context, ArrayList<Product> products) {
    super(context, 0, products);
}



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

    final ViewHolder viewHolder;
    final Product product = getItem(position);

    if (convertView == null) {
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.layout_sliders, parent, false);

        viewHolder = new ViewHolder();
        viewHolder.textViewProductIndex = (TextView) convertView.findViewById(R.id.textViewIndex);
        viewHolder.textViewProductName = (TextView) convertView.findViewById(R.id.textViewProductName);
        viewHolder.textViewQuantity = (TextView) convertView.findViewById(R.id.textViewQuantity);
        viewHolder.imageViewADD = (ImageView) convertView.findViewById(R.id.buttonAdd);
        viewHolder.imageViewFILL = (ImageView) convertView.findViewById(R.id.buttonFill);
        viewHolder.imageViewCheck = (ImageView) convertView.findViewById(R.id.buttonCheck);

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



    if (produto != null) {

        viewHolder.textViewProductIndex.setText(product.getProductIndex());
        viewHolder.textViewProductName.setText(product.getProductName());

        viewHolder.imageViewADD.setTag(position);
        viewHolder.imageViewFILL.setTag(position);
        viewHolder.imageViewCheck.setTag(position);
        viewHolder.textViewProductIndex.setTag(position);
        viewHolder.textViewProductName.setTag(position);
        viewHolder.textViewQuantity.setTag(position);

        viewHolder.imageViewADD.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewHolder.textViewQuantity.setText("TESTING");
            }
        });

    }
    return convertView;
}

class ViewHolder {
    private TextView textViewProductIndex;
    private TextView textViewProductName;
    private TextView textViewQuantity;
    private ImageView imageViewADD;
    private ImageView imageViewFILL;
    private ImageView imageViewCheck;
}}

Other doubts:

-What do I really have to pass on the setTag()?

-Should I handle the clickListeners after the setApdapter on the onItemClickListener of the ListView instead?


Solution

  • There are three issues you raised here. Let me try to explain it this way.

    A) On the view reusability.

    Say you have 100x products in your array. However only 5x products are displayed on the screen at any point in time. So only 5x layouts are inflated by the listview. They are reused as you scroll (Note im simplifying this a little by ignoring caching mechanisms).

    What happens is that if you set the color on the "1st view" to orange for the 1st product. By the time the 6th product reuses that "1st view", which is still orange.

    You need to set a property boolean stated for all your 100x products, so you can set the color of the view based on that property, each time they come into view.

    B) setTag is optional. But it is useful if you see the answer to question C).

    C) There are generally two ways to set the onclick for your listview. - 1st way is to set an onclick to EACH view everytime they appear. So if you scroll from the 1st product to the 100th product, you would have set 100 times. Then when you scroll back up, you need to create an onclick() to each view because they would have been overridden as you scroll.

    A 2nd way is to create a shared clicker. you can set this into each view. So effectively only 1 instance of the listener is made

    final OnClickListener listener = new OnClickListener() { 
        void onClick(View view) { 
            Product product = (Product)view.getTag();
            // do somethign
        } 
    }
    

    Try this

    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    public class ProductAdapter extends ArrayAdapter<Product> {
    
        private final LayoutInflater inflater;
        private Context mContext;
        private ArrayList<Product> mProducts;
    
        final String[] switch = new String[2] { "on", "off" };
    
        public ProductAdapter(Context context, ArrayList<Product> products) {
            super(context, 0, products);
            this.mContext = context;
            this.mProducts = products;
    
            inflater = mContext.getLayoutInflater();
        }
    
        public class ViewHolder {
            private final TextView textViewProductIndex;
            private final TextView textViewProductName;
            private final TextView textViewQuantity;
            private final ImageView imageViewADD;
            private final ImageView imageViewFILL;
            private final ImageView imageViewCheck;
    
            private byte selected = 0;
    
            public ViewHolder(View view) {
                this.mTextViewProductIndex = (TextView) v.findViewById(R.id.textViewIndex);
                this.mTextViewProductName = (TextView) v.findViewById(R.id.textViewProductName);
                this.mTextViewQuantity = (TextView) v.findViewById(R.id.textViewQuantity);
                this.mImageViewADD = (ImageView) v.findViewById(R.id.buttonAdd);
                this.mImageViewFILL = (ImageView) v.findViewById(R.id.buttonFill);
                this.mImageViewCheck = (ImageView) v.findViewById(R.id.buttonCheck);
                view.setTag(this);
            }
        }
    
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.layout_sliders, parent);
                new ViewHolder(convertView);
            }
    
            final ViewHolder holder = (ViewHolder) convertView.getTag();
    
            holder.textViewProductIndex.setText(mProducts.get(position).getProductIndex());
            holder.textViewProductName.setText(mProducts.get(position).getProductName());
            holder.imageViewADD.setOnClickListener(listener);
    
            holder.mTextViewQuantity(switch[holder.selected]);
    
            return convertView;
        }
    
        final OnClickListener listener = new OnClickListener() {
            void onClick(View view) {           
                final ViewHolder holder = (ViewHolder) convertView.getTag();
                holder.selected ^= 1;
                holder.mTextViewQuantity.setText(switch[holder.selected]);
            }       
        }
    }
    

    Another way would be to set in the ItemClickListener of the listview. However, theres less flexibility in that as compared to the 2nd way above.

    Hope this helps