Edit Text in custom List View Loses Value on Scrol

2019-04-15 18:01发布

问题:

I know it's duplicate question, but I didn't find proper answer. In my custom list-view having a two textview and one Editext, when I inserted a value in edit text and scroll then I loss value of edited text. And when I pass string value for an adapter through list adapter then it will show blank edit text.

Here my Adapter code:

class CreateAdapter extends ArrayAdapter<String> {
        String[] strItecode=null;
        String[] strItem;
        String[] strQuantity,text;
        Context context;
        int temp;
        CreateAdapter(Context context, String[] strItemcode, String[] strItem,
                String[] strQauntity) {
            super(context, R.layout.create_list_item, R.id.txtItemcode, strItemcode);
            this.context = context;
            this.strItecode = strItemcode;
            this.strItem = strItem;
            this.strQuantity = strQauntity;
            text= new String[strItem.length];
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            temp=position;
            LayoutInflater mInflater = (LayoutInflater) context
                    .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
            if (convertView == null) {

                holder = new ViewHolder();
                convertView = mInflater
                        .inflate(R.layout.create_list_item, parent, false);

                holder.txtItecode = (TextView) convertView
                        .findViewById(R.id.txtItemcode);
                holder.txtItem = (TextView) convertView
                        .findViewById(R.id.txtItem);
                holder.editQuantity = (EditText) convertView
                        .findViewById(R.id.editcreateQuantity);
                holder.editQuantity.setTag(position);

                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
          holder.editQuantity.addTextChangedListener(new TextWatcher() {

              @Override
              public void onTextChanged(CharSequence s, int start,
                      int before, int count) {
                  text[temp] = s.toString();
              }

              @Override
              public void beforeTextChanged(CharSequence s, int start,
                      int count, int after) {

              }

              @Override
              public void afterTextChanged(Editable s) {

              }
          });
            holder.txtItecode.setText(strItecode[position]);
            holder.txtItem.setText(strItem[position]);
            holder.editQuantity.setText(text[temp]);

            return convertView;
        }

        class ViewHolder {
            TextView txtItecode;
            TextView txtItem;
            EditText editQuantity;
        }
    }

I'm working on it past 3 days, how I can fix this problem?

回答1:

In addition to some of the comments about possibly using a ScrollView, the real issue with your code is how the ListView's Adapter is reusing the Views in conjunction with your TextWatcher(s).

Every single time a row needs to be displayed, the getView() method on that position will be called. The adapter can, and will, reuse the actual Views you've inflated in previous traversals to save memory.

That is usually a good thing, but is going to have consequences when you're working with EditTexts and TextWatchers.

First, holder.editQuantity.addTextChangeListener() is just going to keep adding a new TextWatcher on every layout pass. That's bad and is partly responsible for your issue.

Secondly, once a view is being reused, you're actually changing the text on it after setting the TextWatcher, which will trigger both the old (if there is one) and the new TextWatchers' onTextChanged() methods and change your stored values, which in turn is messing up your Views.

If you want to go the ListView/Adapter route with this, your solution will be to have a global TextWatcher variable, remove the TextWatcher from the View before setting the text in getView() (so that it doesn't fire its events when you don't want it to), and then re-adding it after you've set the text. However, since you can't keep creating new TextWatchers (as that will prevent you from removing the old ones as well as experience the same issue with messed up values), you'll also need a way to link to the appropriate position in your text[], since you won't be able to point to it from the getView() method. However, you can set the EditText's FocusChangeListener to determine when the View has focus (ie. it's being edited) and set the appropriate editing position from that)

E.g.

private int editingPosition = 0;
private TextWatcher watcher = new TextWatcher() {
          public void onTextChanged(CharSequence s, int start, int before, int count) {
              text[editingPosition] = s.toString();
          }
          public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
          public void afterTextChanged(Editable s) { }
      };

public View getView(int position, View convertView, ViewGroup parent) {

       // Before this is fine

      holder.editQuantity.removeTextChangedListener(watcher);

      holder.editQuantity.setText(text[temp]);

      holder.editQuantity.setOnFocusChangeListener(new OnFocusChangeListener() {       
          public void onFocusChange(View v, boolean hasFocus) {
            if(hasFocus) editingPosition = position;
          }
      });

      holder.editQuantity.addTextChangedListener(watcher);

      // The rest is good, just make sure to remove the call to setting the EditText's text at the end

      return convertView;
}