ListView's View recall causes problems such as listening to checkbox state changes

  • 2020-05-09 19:14:27
  • OfStack

We talked earlier about when custom Adapter is passed to ListView, because View is recycled from ListView, you need to be aware of the problem when an ListView list item contains a control with a status id. Details visible before post [url = custom Adapter implementation ListView state control considerations such as boxes, / / www ofstack. com article / 33425. htm
So once again, let's do a problem that I had with two lines of code in opposite directions.
Each line of View in my ListView contains 1 ImageView, TextView, CheckBox. When 1 or 1 line of CheckBox in ListView is selected, show Button above ListView; otherwise, hide it. Therefore, you need to set up a listener for CheckBox in View per row. I use the OnCheckedChangeListener listener in CheckBox, which is triggered when the state of CheckBox changes. Take a look at some of the key code in Adapter's getView method that I customized to ListView:
This is the inner class used in the getView method:
 
static class ViewHolder { 
public ImageView imageView; 
public TextView textView; 
public CheckBox checkBox; 
} 

This is the code for recycling View in the getView method using the ListView recovery mechanism:
 
public View getView(int position, View convertView, ViewGroup parent) { 
ViewHolder viewHolder; 
if (convertView == null) { 
convertView = inflater.inflate(R.layout.searchitem, null); 
viewHolder = new ViewHolder(); 
viewHolder.imageView = (ImageView) convertView 
.findViewById(R.id.searchitemimage); 
viewHolder.textView = (TextView) convertView 
.findViewById(R.id.searchitemtext); 
viewHolder.checkBox = (CheckBox) convertView 
.findViewById(R.id.searchitemcheckbox); 
convertView.setTag(viewHolder); 
} else { 
// Log.i(CodeUtils.SEARCHTAG, "view is reuse"); 
viewHolder = (ViewHolder) convertView.getTag(); 
} 

Next is the code to set the display status and listener of checkbox:
 
viewHolder.checkBox 
.setOnCheckedChangeListener(new SearchItemOnCheckedChangeListener( 
position, state)); 
viewHolder.checkBox.setChecked(state[position]); 

As mentioned before, because of the recall of ListView, it is necessary to use an array or list to record the state of checkbox in each data item. Here, state is an array of boolean as long as the ListView list, which is used to record the state that checkbox should display on the data identified by each position (that is, id of each list item data). The initial state is false. To construct the checkbox listener, you need to pass the current position of View, as well as the state array state of the entire list checkbox. Here is the code for the checkBox state change listener:
 
public class SearchItemOnCheckedChangeListener implements 
OnCheckedChangeListener { 
private int id; 
private Boolean[] state; 
public SearchItemOnCheckedChangeListener(int id, Boolean[] state) { 
this.id = id; 
this.state = state; 
} 
@Override 
public void onCheckedChanged(CompoundButton buttonView, 
boolean isChecked) { 
state[id] = isChecked; 
if (isChecked) { 
checkedCount++ ;  
}else{ 
checkedCount-- ;  
} 
if (checkCoutn>0) { 
searchButton.setVisibility(Button.INVISIBLE); 
} else { 
searchButton.setVisibility(Button.VISIBLE); 
} 
} 
} 
} 

The integer with checkedCount starting at 0 is used to record the number of selected checkboxes. searchButton is the button that determines whether to show or hide based on checkbox.

Above the implementation of the entire logic function code. As I said at the beginning, this is a problem that I caused because of the ListView recall mechanism and the opposite position of the two lines of code. The opposite position of the two lines of code will lead to completely different results, which refers to the two lines of code that set checkbox listener and state. At first, my order is:
 
viewHolder.checkBox.setChecked(state[position]); 
viewHolder.checkBox.setOnCheckedChangeListener(new SearchItemOnCheckedChangeListener(position, state)); 

The problem with this order is that when I pull the list, the state of the list items that are hidden because of the pull will be changed to false. This is amazing, because I've split a state array to record the state of each checkbox, and there's only one possibility, after all, that the value in the state array has changed, and the change in the state array is in OnCheckedChangeListener. It took Debug several hours to figure out the problem was that the two lines of code were in sequence.

The reason for this is again the recall mechanism of ListView. If my ListView can only display 10 View at most, then getView is initially called 10 times to construct 10 new View (including setting listeners on checkbox). When I drop down the list to show the 11th list item, the top 1 list item is hidden and getView is called again, but the getView parameter will return the View of the first list item that was just hidden and change the data to this View as the upcoming 11th View. Here's the problem. I put the checkbox.setChecked () method call in front of the Settings listener. At this point, because the state of checkbox has been changed, it is bound to cause the listener that triggers the state change. Attention! Since the 11th View is recycled from the hidden 1st View, although the next line of code to set the listener has not been executed, it actually has a status listener, which is set as this View or as the 1st View. At that time the listener Settings changed the data for item 1, not item 11. Therefore, of course, item 11 was not changed correctly, but the innocent item 1 was. If I reverse the order of the two lines of code, change the listener first, and then set the state, the resulting listener will naturally be the new listener, and the logic will be correct.

Related articles: