3

Here is my Array adapter. It assigns each switch button in the list with a listener that is dependant on the id given by the database (passed using devices.id). But for some reason, the switch listener is getting the correct states from the switches.

For debugging purpose, I'm just using the same on change listener regardless of the state. So whether I turn it ON or OFF, it runs the same function.

My problem is sometimes when I turn ON 1st switch, 7th switch is turned ON, n when I turn ON the 2nd switch, 8th switch is turned ON. There's something wrong I'm doing, but I cant really figure out. The 6th, 7th and 8th switches don't work properly.

You can see in the LCD, When I turn ON the switches in order, it must give me the output "12345678". But its not. Instead it gives "12345112".

Edit: Answer given by Emil Adz works. Gives me output "12345678". But I have one problem. If I turn ON switch1, scroll down till its not visible and scroll up, the switch resets to OFF. Why is it so?

Wireless Controller Screenshot Wireless Controller Screenshot LCD Output

public class DevListAdapter extends ArrayAdapter<Devices>{

    Context context; 
    int layoutResourceId;    
    Devices data[] = null;
    private TextView txt = null;
    
    public DevListAdapter(Context context, int layoutResourceId, Devices[] data) {
        super(context, layoutResourceId, data);
        this.layoutResourceId = layoutResourceId;
        this.context = context;
        this.data = data;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("WCAM","GetView");
        View row = convertView;
        DeviceHolder holder = null;
        Devices devices = data[position];
        String id = devices.id;

        Log.d("WCAM","Inflator");
        if(row == null)
        {
            Log.d("WCAM","True");
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);

            holder = new DeviceHolder();
            holder.devTxt = (TextView)row.findViewById(R.id.devdesc);
            holder.nameTxt = (TextView)row.findViewById(R.id.devname);
            holder.toggleSwitch = (Switch)row.findViewById(R.id.toggleswitch);
            holder.txt = (TextView) row.findViewById(R.id.debug);
            final int i;
            
            if(id.matches("1")) {
                i = 0x31;
            }
            else if(id.matches("2")) {
                i = 0x32;
            }
            else if(id.matches("3")) {
                i = 0x33;
            }
            else if(id.matches("4")) {
                i = 0x34;
            }
            else if(id.matches("5")) {
                i = 0x35;
            }
            else if(id.matches("6")) {
                i = 0x36;
            }
            else if(id.matches("7")) {
                i = 0x37;
            }
            else {
                i = 0x38;
            }
            holder.toggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    Control._service.write(i);
                }
             });
            
            row.setTag(holder);
        }
        else
        {
            Log.d("WCAM","False");
            holder = (DeviceHolder)row.getTag();
        }
        
        holder.devTxt.setText(devices.dev);
        holder.nameTxt.setText(devices.name);
        holder.txt.setText(id);
        
        return row;
    }
    
    static class DeviceHolder
    {
        TextView devTxt;
        TextView txt;
        TextView nameTxt;
        Switch toggleSwitch;
    }
}

devices is an object of Devices.java

public class Devices {
    public String dev;
    public String name;
    public String id;
    public Devices(){
        super();
    }
    
    public Devices(String _id, String dev, String name) {
        super();
        this.dev = dev;
        this.name = name;
        this.id = _id;
    }
}
Xpleria
  • 5,472
  • 5
  • 52
  • 66

3 Answers3

1

Try it like this:

public class DevListAdapter extends ArrayAdapter<Devices>{

Context context; 
int layoutResourceId;    
Devices data[] = null;
private TextView txt = null;

public DevListAdapter(Context context, int layoutResourceId, Devices[] data) {
    super(context, layoutResourceId, data);
    this.layoutResourceId = layoutResourceId;
    this.context = context;
    this.data = data;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.d("WCAM","GetView");
    DeviceHolder holder = null;
    Devices devices = data[position];
    String id = devices.id;

    Log.d("WCAM","Inflator");
        Log.d("WCAM","True");
        LayoutInflater inflater = ((Activity)context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, parent, false);

        holder = new DeviceHolder();
        holder.devTxt = (TextView)row.findViewById(R.id.devdesc);
        holder.nameTxt = (TextView)row.findViewById(R.id.devname);
        holder.toggleSwitch = (Switch)row.findViewById(R.id.toggleswitch);
        holder.txt = (TextView) row.findViewById(R.id.debug);
        final int i;

        if(id.matches("1")) {
            i = 0x31;
        }
        else if(id.matches("2")) {
            i = 0x32;
        }
        else if(id.matches("3")) {
            i = 0x33;
        }
        else if(id.matches("4")) {
            i = 0x34;
        }
        else if(id.matches("5")) {
            i = 0x35;
        }
        else if(id.matches("6")) {
            i = 0x36;
        }
        else if(id.matches("7")) {
            i = 0x37;
        }
        else {
            i = 0x38;
        }
        holder.toggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Control._service.write(i);
            }
         });

    holder.devTxt.setText(devices.dev);
    holder.nameTxt.setText(devices.name);
    holder.txt.setText(id);

    return row;
}

Basically for some reason I encountered this problem several times. When you use convertView then all the list is going insane. Be aware of the fact that it derives performance but I didn't find a proper solution for this problem. What more when the list is not that big it doesn't change much.

UPDATE: First take a look at this guide:

Multi-clickable ListView

As with the check box state there you will see:

    //setting data into the the ViewHolder.
    holder.title.setText(RowData.getName());
    holder.checked.setChecked(RowData.isChecked());

    //return the row view.
    return convertView;

So in your case after this code:

    holder.devTxt.setText(devices.dev);
    holder.nameTxt.setText(devices.name);
    holder.txt.setText(id);

you should add something like:

   holder.toggleSwitch.setChecked(devices.isChecked());

and in your onCheckedChanged persist the state changes.

Emil Adz
  • 40,709
  • 36
  • 140
  • 187
  • This works. Gives me output "12345678". But I have one problem. If I turn ON switch1, scroll down till its not visible and scroll up, the switch resets to OFF. Why is it so? – Xpleria May 05 '13 at 17:57
  • what happens when you change the toggle button? what is "Control._service.write(i);"? you need to note somewhere that you are changing the togglebutton's state and when you recreate this row (when it visible again to get this setting and apply it to the row in the correct position) – Emil Adz May 05 '13 at 18:02
  • I'm totally new to android. Could you plz give me a sample code to recreate the row with the new state of the button or at least a reference? Thanks `Control._service.write(i);` is a method in the `Control` activity that sends the data to microcontroller via bluetooth. – Xpleria May 05 '13 at 18:08
  • What is `isChecked()`? How do I implement it..? – Xpleria May 05 '13 at 18:45
  • usually when you use ArrayAdapter there is an array of objects, when each object in the array is assigned to a row in the list. isCheck should be placed as a Boolean parameter in the object that represent a list item. check the example link I posted. and see how the checkbox is implemented. – Emil Adz May 05 '13 at 18:47
  • I'm unable to use `devices.isChecked()`. Says its undefined for the type Devices. – Xpleria May 05 '13 at 19:05
  • what is the devices object? – Emil Adz May 05 '13 at 19:06
  • I've updated my question with the Devices class. let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29445/discussion-between-neil-martin-and-emil-adz) – Xpleria May 05 '13 at 19:14
1

I had the same kind of problem with my switch view. After a lot of search and reading, I figure out that this problem is result of a sort of optimization of the views promoted by Android. It try to reuse the views objects. That really works, but if you do not pay attention, or just don't know the behavior (like me), some pretty weird results happens.

Anyway, I find this simple solution:

A store my views in a ViewHolder and programmed the onCheckedChanged event, just like you. The trick is, before set if the switch is checked or not and define the CheckedChange listener, I simply reset the callback with null, this ensures that the event of another switch isn't triggered.

Here is the snippet:

...
viewHolder.switchView.setOnCheckedChangeListener(null);
viewHolder.switchView.setChecked(myObject.getStatus() > 0);
...
viewHolder.switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                ...
            }
        });

myObject is a final object that has the Cursor data stored.

And regardless of the switch will be checked or not, you have to call the setChecked method.

Hope it helps someone!

Pedro Acácio
  • 139
  • 1
  • 5
0

Your ListView is currently associating the state of a switch with the position of a row, rather than with the actual content of a row. When the user scrolls through your ListView, and it's contents are recycled, it's putting in switch values based on position.

To fix this, you need to save the state of a switch in such a way that it is associated with an actual row. Android: Problem With ListViews and CheckBoxes deals with the exact same issue, but using checkboxes. It is extremely useful for resolving these sorts of problems

Community
  • 1
  • 1
Workforce7
  • 41
  • 1
  • 1
  • 6