3

Here is my custom arrayadapter class, I want to change the image resource of a button in each row when I click ont it, it works but an other button in an other row also changes. thanks for your help !

public class Coursadapter extends ArrayAdapter<String> {

    Context context;
    int layoutResourceId;
    ArrayList<String> data = null;
    WeatherHolder holder;

    public Coursadapter(Context context, int layoutResourceId,
            ArrayList<String> data) {
        // super(context, layoutResourceId, data, coeff);
        super(context, layoutResourceId, data);
        this.layoutResourceId = layoutResourceId;
        this.context = context;
        this.data = data;

    }

    public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;

        if (row == null) {
            LayoutInflater inflater = ((Activity) context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, null);

            holder = new WeatherHolder();
            holder.name = (TextView) row.findViewById(R.id.item_cours_name);
            holder.b = (ImageButton) row.findViewById(R.id.button);
            holder.b.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    WeatherHolder w = (WeatherHolder) v.getTag();
                    w.b.setImageResource(R.drawable.butgreen);
                }
            });
            row.setTag(holder);

        } else {
            holder = (WeatherHolder) row.getTag();
        }

        holder.b.setTag(holder);
        String name1 = data.get(position);
        holder.name.setText(name1);

        return row;
    }

    static class WeatherHolder {

        TextView name;
        ImageButton b;
    }
}
Mouna Kam
  • 61
  • 1
  • 7
  • Possible duplicate of [Custom arrayadapter and onclicklistener for a button in a row](http://stackoverflow.com/questions/21161959/custom-arrayadapter-and-onclicklistener-for-a-button-in-a-row) – Anirudh GP Mar 08 '17 at 18:30

3 Answers3

1

You can declare variable int that will hold the position of selected item and

public void onItemClick(AdapterView<?> adapter, View arg1, int position, long id) {
            mSelectedItem = position;
            mAdapter.notifyDataSetChanged();
    }

and in your custom adapter getView method check the position and change the image of your button

public View getView(int position, View convertView, ViewGroup parent) {
        if (position == mSelectedItem) {
        // set your image
    }

Look i try this code with me and work , implements OnClickListener in your activity

public class MainActivity extends Activity  implements OnClickListener

and in your getView method

public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;

        if (row == null) {
            LayoutInflater inflater = ((Activity) context)
                    .getLayoutInflater();
            row = inflater.inflate(layoutResourceId, null);

            holder = new WeatherHolder();
            holder.name = (TextView) row.findViewById(R.id.item_cours_name);
            holder.b = (ImageButton) row.findViewById(R.id.button);
            holder.b.setOnClickListener(MainActivity.this);

and the implement the Onclick method set the resource

@Override
public void onClick(View v) {
    WeatherHolder w = (WeatherHolder) v.getTag();
    w.b.setImageResource(android.R.drawable.star_big_on);
}
mohammed momn
  • 3,230
  • 1
  • 20
  • 16
  • Thanks but I don't have an OnItemClick Method in my arrayadapter where can I set that variable ? – Mouna Kam Jan 17 '14 at 20:52
  • in your class as global variable – mohammed momn Jan 17 '14 at 20:55
  • You don't have an `onItemClick()` method because that would be in an `OnItemClickListener`, which you would only use if you wanted the action to occur when you click anywhere in your Listview item. You only want it to happen when you click on a button, so this won't really help you. – sigmabeta Jan 17 '14 at 21:04
  • Yes I can't get the postion of the item with an OnClickListener – Mouna Kam Jan 17 '14 at 21:06
  • look i try your code and when i click on image button one button change only not all image buttons this i right behavior – mohammed momn Jan 17 '14 at 21:14
0

The problem with your code is that your OnClickListener is being set up in the wrong place.

Consider what happens as a result of the if/else statement in getView(). If convertView passed into that method is null, it is likely the case that the View (and the ListView containing it) is being created for the first time - for example, you are launching the activity that contains that ListView. If it is not null, that means you are recycling a view that was, but is no longer, on-screen.

If you look carefully, you'll see that your OnClickListener only gets set in that first case - a brand new view. Once you scroll around a bit, your listener will never be correctly attached to those views that are coming from off-screen.

A better getView() method would be:

public View getView(int position, View convertView, ViewGroup parent) {
    View row = convertView;

    if (row == null) {
        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, null);

        holder = new WeatherHolder();
        holder.name = (TextView) row.findViewById(R.id.item_cours_name);
        holder.b = (ImageButton) row.findViewById(R.id.button);

        row.setTag(holder);

    } else {
        holder = (WeatherHolder) row.getTag();
    }

    String name1 = data.get(position);
    holder.name.setText(name1);

    holder.b.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {                    
                ((ImageButton)v).setImageResource(R.drawable.butgreen);
            }
        });

    return row;
}

Edit: Added in the other fix from my below comment.

sigmabeta
  • 1,174
  • 1
  • 12
  • 28
  • Thanks but I tried this and I have the same problem, another button in another row is changed too – Mouna Kam Jan 17 '14 at 21:05
  • The other issue is in your `OnClick()` method, you are doing a lot of unnecessary work. The `View` passed to it is the `Button` the user clicked. That's the one you want to change the color of, right? So you don't need to use `getTag()`, and you definitely don't need to use `setTag()`. Just call `v.setImageResource(R.drawable.butgreen);` – sigmabeta Jan 17 '14 at 21:10
  • I've removed the setTag() getTag(), I'm just using an OnClickListener after the else with ((ImageView)v).setImageResource(R.drawable.butgreen); and the same problem happens – Mouna Kam Jan 17 '14 at 21:17
0

The reason why this happens is because the adapter recycles the views on scrolling.

For example, suppose you have a total of 15 rows and 5 rows fit on the screen at a time. The if(row == null) part executes for the first 5 rows only. When you scroll down to the 6th row, it uses the View of the 1st row. This is why you are supposed to set the content of your View after the if-else clause. So now, when the 6th row comes into the screen, row is not null. It holds the View of the 1st row. When it executes holder.name.setText(name1);, it is replacing the name of the 1st row with the name of the 6th row. Similarly, the 7th row uses the view of the 2nd row and so on.

Now, suppose you click on the button in the 1st row. The View for the 1st row changes and w.b.setImageResource(R.drawable.butgreen); sets the image resource of the button in the 1st row. When you scroll to the 6th row, it uses the View of the 1st row. But you are setting only holder.name to the name of the 6th row. The image resource of the button in the 1st row is going to remain there, hence causing your problem.

Solution: Create another ArrayList and store the status of the button in this. So in your case, I'm assuming the buttons are either "red" or "green" in color. You could store either "red" or "green" at each position signifying what color each button is currently (you can initialize this ArrayList to red). Then, when each row loads, you can set the image resource along with the name of that row. Take a look at the code below. I have put comments to the lines I added.

public class Coursadapter extends ArrayAdapter<String> {

Context context;
int layoutResourceId;
ArrayList<String> data = null;
WeatherHolder holder;
ArrayList<String> colors; // Stores the color of each button

public Coursadapter(Context context, int layoutResourceId, ArrayList<String> data) {
    // super(context, layoutResourceId, data, coeff);
    super(context, layoutResourceId, data);
    this.layoutResourceId = layoutResourceId;
    this.context = context;
    this.data = data;
    this.colors = new ArrayList<>(Collections.nCopies(data.size(), "red")); // initialize all the buttons to red color
}

public View getView(final int position, View convertView, ViewGroup parent) {
    View row = convertView;


    if(row == null)
    {
        LayoutInflater inflater = ((Activity)context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, parent, false);

        holder = new WeatherHolder();
        holder.name = (TextView)row.findViewById(R.id.item_cours_name);
        holder.b=(ImageButton) row.findViewById(R.id.button);
        holder.b.setTag(holder);
        row.setTag(holder);
    }
    else
    {
        holder = (WeatherHolder)row.getTag();
    }

    if(colors.get(position).equals("red")) {
        holder.b.setImageResource(R.drawable.butred); // set the colors of the button to red or whatever you need
    } else {
        holder.b.setImageResource(R.drawable.butgreen); // set the colors of the button to green
    }

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

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            WeatherHolder w = (WeatherHolder) v.getTag();
            //w.b.setImageResource(R.drawable.butgreen);
            colors.set(position, "green"); // update the ArrayList colors
        }
    });
    String name1 = data.get(position);
    holder.name.setText(name1);


    return row;
}

static class WeatherHolder
{

    TextView name;
    ImageButton b;
}
}
Anirudh GP
  • 345
  • 1
  • 4
  • 15
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/15461115) – morten.c Mar 08 '17 at 18:17
  • The question I have linked here is the exact same as this one. I could not find an option to report it as a duplicate so I linked the answer to the other question here. Should I include the essential parts here also? – Anirudh GP Mar 08 '17 at 18:20
  • "I could not find an option to report it as a duplicate so I linked the answer to the other question here." => You can flag a question as duplicate by clicking the `flag` button below the question, then `Should be closed...` --> `duplicate of...`. "Should I include the essential parts here also?" => Yes, this is always a good idea, since the other question can get deleted (same with external links). – morten.c Mar 08 '17 at 18:25
  • Ok I have flagged it as a duplicate and will edit my answer. Thanks – Anirudh GP Mar 08 '17 at 18:31