2

I have a listView in my app. For each element in the listView, it has a Switch. Previously I implemented an onClickListener for the Switch inside the listView Adapter's getView method. However, I found that although it does work when user clicks on the switch, it does not work when user slide on the Switch. Then I try to change the onClickListener to onCheckedChangeListener. It works when user click or slide on the Switch, however, when I scroll the the listView so that the element disappear, I found that the Switch which is originally checked become unchecked.

Could you help? Below is my code for the getView method in the listView Adapter:

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
    class viewHolder {
        public TextView tvSceneName;
        public Switch swSelectSwitch;
    }
    final viewHolder holder;

    final View v = convertView;

    // Use ViewHolder to avoid findViewById each time the user scroll
    if (convertView == null) {
        holder = new viewHolder();
        convertView = mInflater.inflate(R.layout.scene_list_element, parent, false);
        holder.tvSceneName = (TextView) convertView.findViewById(R.id.tvSceneName);
        holder.swSelectSwitch = (Switch) convertView.findViewById(R.id.swScene);
        convertView.setTag(holder);
    } else {
        holder = (viewHolder) convertView.getTag();
    }


    if(holder.swSelectSwitch != null) {

        // Set the switch to correct on/off status
        holder.swSelectSwitch.setChecked(mDataSource.get(position).get_isOn());

        // Set up On checked change listener for the switch
        holder.swSelectSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton toggleButton, boolean isChecked) {

                // Set the new status to arraylist
                Switch sw = (Switch) v.findViewById(R.id.swScene);
                MainActivity.sceneArrayList.get(position).set_isOn(sw.isChecked());

        });
    }
}

EDIT: On further test, I found there is bug in my newly added onCheckChangedListener (because I copied some code from the onClickListener). Also I have found a solution, it is to set the onCheckChangedListener to null before I set the Switch by code.

if(holder.swSelectSwitch != null) {

    // Set the switch to correct on/off status
    holder.swSelectSwitch.setOnCheckedChangeListener(null);
    holder.swSelectSwitch.setChecked(mDataSource.get(position).get_isOn());

    // Set up On checked change listener for the switch
    holder.swSelectSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton toggleButton, boolean isChecked) {

            // Set the new status to arraylist
            mDataSource.get(position).set_isOn(isChecked);

    });
}
Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79
eepty
  • 716
  • 3
  • 10
  • 28
  • 1
    You are using two lists. They have the same reference? If not you are updating a different list so after you scroll it's clear why your checkbox is not updated. Other thing is why you don't use the params from the onCheckedChanged listener instead of using findviewbyId – Alex Nov 08 '16 at 07:50
  • @Alex You are right, I found that I should use the isChecked parameters. I use the findviewbyId because I just copy the code from a onClickedListner. The onClickedListener does not have this parameter. And the lists are same. In fact I am not very clear about how to use the listView when it has many components and when the data source is complicated. – eepty Nov 08 '16 at 09:45

2 Answers2

1

The point of the ViewHolder pattern is that the Views are getting recycled.

You're fetching the checked state from mDataSource, but not updating it, so when the View gets recycled the Switch returns to its original (unchecked) state.

You should update the state in mDataSource when a change occurs:

@Override
public void onCheckedChanged(CompoundButton toggleButton, boolean isChecked) {

    // Set the new status to arraylist
    MainActivity.sceneArrayList.get(position).set_isOn(isChecked);

    // updating mDataSource
    mDataSource.get(position).set_isOn(isChecked);
}
earthw0rmjim
  • 19,027
  • 9
  • 49
  • 63
0

You should delete the line if(holder.swSelectSwitch != null) { : when you scroll, the views are not destroyed but recycled, so when getView is called after scrolling, the convertView is not null. When convertView is not null, you are then reusing the holder you already created and put in the view tag.

ThomasV
  • 779
  • 1
  • 5
  • 20