问题
I've read a few threads about this problem, but couldn't find a thorough answer. I have a ListView with 3 rows, each contains a TextView and an EditText, and a custom adapter that extends BaseAdapter.
Here's the adapter's getView function:
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if(convertView == null) {
LayoutInflater inflater = mActivity.getLayoutInflater();
convertView = inflater.inflate(R.layout.settings_column, null);
mTxtValue = (EditText) convertView.findViewById(R.id.settings_value);
mTxtValue.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (tryParseInt(s.toString())) {
RowsList.get(position).setDuration(Integer.parseInt(s.toString()));
System.out.println("position: " + position + ", value: " + s.toString());
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
ColorColumn colorColumn = RowsList.get(position);
mTxtValue.setText(String.valueOf(colorColumn.getDuration()));
return convertView;
}
As you can see, I try to update a list of of ColorColumn named RowsList with the EditText value, everytime the value is changed. For some reason, the onTextChanged method is called too many times and hence puts false data in the list. The inputType of the EditText is android:inputType="number|textNoSuggestions"
, as suggested in another thread.
This is the log that appears when the activity is started and the ListView is being populated:
10-27 19:30:15.238 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 1
10-27 19:30:15.242 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 2
10-27 19:30:15.244 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:15.317 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:15.325 19845-19845/com.busalert.www.busalert I/System.out: position: 1, value: 2
10-27 19:30:15.333 19845-19845/com.busalert.www.busalert I/System.out: position: 2, value: 3
10-27 19:30:15.346 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:15.350 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 2
10-27 19:30:15.353 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:15.388 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:15.394 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 2
10-27 19:30:15.398 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
As you can, the first appearance of each position is the correct one, but there are 9 extra calls.
This is the log that appears when the user first enter one the EditText's:
10-27 19:30:21.226 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:21.230 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 2
10-27 19:30:21.231 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:21.356 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:21.357 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 2
10-27 19:30:21.363 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:21.369 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 19:30:21.370 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 2
10-27 19:30:21.376 19845-19845/com.busalert.www.busalert I/System.out: position: 0, value: 3
Again, 9 extra calls (there should be 0 calls, since nothing really changed).
From here and on, each change results in one call as required.
UPDATE: I created a boolean array to indicate whether the TextWatcher was already added to each EditText, and thus I ensure each of them has only one. After this addition, there were 6 calls (3 extra) when the activity started and no calls when I first clicked an EditText. This is the new log (2, 3 and 4 are superfluous):
10-27 20:02:49.765 29637-29637/com.busalert.www.busalert I/System.out: position: 0, value: 1
10-27 20:02:49.767 29637-29637/com.busalert.www.busalert I/System.out: position: 0, value: 2
10-27 20:02:49.769 29637-29637/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 20:02:49.827 29637-29637/com.busalert.www.busalert I/System.out: position: 0, value: 3
10-27 20:02:49.834 29637-29637/com.busalert.www.busalert I/System.out: position: 1, value: 2
10-27 20:02:49.840 29637-29637/com.busalert.www.busalert I/System.out: position: 2, value: 3
回答1:
Problem is that the LisView elements are being recycled and therefore the old TextWatcher is still attached after recycling the row. Therefore every time getView is called a new TextWatcher is added while the old ones remain attached to the EditText. Unfortunately there is no function to remove all old TextWatchers attached, therefore the only solution is to create a custom EditText where you keep a reference to all TextWatcher and then create a custom function to remove them all when recycling the View in getView.
回答2:
Another solution is to check if the field (EditText or TextView) isDirty.
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mTextView.isDirty())
doWork();
}
来源:https://stackoverflow.com/questions/33375484/textwatcher-ontextchanged-called-multiple-times