4

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
Neria Nachum
  • 1,519
  • 1
  • 20
  • 37
  • does it work the same when using EditText outside ListView / adapter item ? – pskink Oct 27 '15 at 18:55
  • Haven't tried myself, but according to other threads (for example [this one](http://stackoverflow.com/questions/17535415/textwatcher-events-are-being-fired-multiple-times)), yes. – Neria Nachum Oct 27 '15 at 18:57
  • but i tried, and no, there is no extra calls, try it by yourself and that way you can find out the culprit – pskink Oct 27 '15 at 19:00
  • Thanks, I'll try it, though it's not solving my problem because I'm willing to use a ListView. – Neria Nachum Oct 27 '15 at 19:01
  • ok first try simple EditText, then use it inside the ListView where adapter has only one Item, this is an investigation – pskink Oct 27 '15 at 19:03
  • Without the ListView it all worked perfectly smooth, and since I got an exact amount of rows, I decided to give up on the ListView and draw the widgets manually in the xml. It's not the cleanest solution but it fits my current requirement. Thanks for the help! – Neria Nachum Oct 27 '15 at 21:19
  • this solution works => http://stackoverflow.com/questions/31844373/saving-edittext-content-in-recyclerview – prasanthMurugan Apr 13 '16 at 05:25

2 Answers2

0

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.

luk2302
  • 55,258
  • 23
  • 97
  • 137
0

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();
            }
havabon
  • 134
  • 7