Просмотр списка с edittext и textwatcher. Textwatcher срабатывает несколько раз (более 3 раз)

У меня есть собственный адаптер списка с одним элементом списка, имеющим три текста редактирования. И у меня есть выбор даты для одного из них. При установке даты соответствующая модель адаптера сохраняется с правильной датой. Но для других текстов редактирования я просто хочу, чтобы введенный текст сохранялся в списке. И я использую textwatcher для выполнения задачи. Моя проблема заключается в том, что наблюдатель за текстом срабатывает несколько раз (точнее, 5 раз) для одной записи в представлении.

Если один из текстов редактирования работает правильно, почему не работает другой? Мне действительно не помешала бы помощь. До сих пор я не смог найти никакого успеха.

public class AddExpensesAdapter extends BaseAdapter{
private Activity activity;
private ArrayList<AddExpensesModel> addExpensesList;
private LayoutInflater inflater;
private  TextView totalAmount;
private ArrayList<String> array_amount= new ArrayList<String>();
private ArrayList<String> array_name= new ArrayList<String>();
public AddExpensesAdapter(Activity activity, ArrayList<AddExpensesModel> addExpensesList)        {
    this.activity = activity;
    this.addExpensesList = addExpensesList;
    this.inflater = (LayoutInflater) this.activity
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public int getCount() {
    return addExpensesList.size();
}

@Override
public Object getItem(int position) {
    return null;
}

@Override
public long getItemId(int position) {
    return 0;
}

public AddExpensesModel getExpenseModel(int position)
{
    return addExpensesList.get(position);
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    final ViewHolder viewHolder;
    if (convertView == null) {
        viewHolder = new ViewHolder();
        convertView = inflater.inflate(R.layout.a_add_expenses_listitem, null);


        viewHolder.expenseName = (EditText) convertView.findViewById(R.id.add_expenses_et_expense_name);
        viewHolder.dateTime = (EditText) convertView.findViewById(R.id.add_expenses_et_date_time);
        viewHolder.amount = (EditText) convertView.findViewById(R.id.add_expenses_et_amount);
        viewHolder.editDate = (ImageView) convertView.findViewById(R.id.add_expenses_edit_date);
        viewHolder.number = (TextView) convertView.findViewById(R.id.add_expenses_tv_serial_number);
        viewHolder.deleteExpense = (ImageView) convertView.findViewById(R.id.add_expenses_delete_expense);
        // viewHolder.totalfriends = (TextView)
        // convertView.findViewById(R.id.eventtotalfriends);

        convertView.setTag(viewHolder);
        viewHolder.ref = position;
    } else {
        viewHolder = (ViewHolder) convertView.getTag();

    }


    final AddExpensesModel addExpenseModel = addExpensesList.get(position);

    viewHolder.expenseName.setText(addExpenseModel.getExpenseName());
    viewHolder.dateTime.setText(addExpenseModel.getDateTime());
    viewHolder.amount.setText(addExpenseModel.getAmount());
    viewHolder.number.setText(""+(position+1)+".");




    viewHolder.editDate.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            try {
                setDate(addExpenseModel);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });

    viewHolder.deleteExpense.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            addExpensesList.remove(position);
            notifyDataSetChanged();
        }
    });


        viewHolder.amount.addTextChangedListener(new TextWatcher() {

            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {

            }

            @Override
            public void afterTextChanged(Editable editable) {

                if(!editable.toString().equals(""))
                {


                                addExpenseModel.setAmount(editable.toString());

                        }


                    notifyDataSetChanged();

            }
        });

    return convertView;
}



public void setDate(final AddExpensesModel model) throws Exception {

    final String dateFormatPattern = "MMM, dd yyyy";
    final SimpleDateFormat sdf = new SimpleDateFormat(dateFormatPattern,
            Locale.getDefault());

    DatePickerDialog.OnDateSetListener date = new DatePickerDialog.OnDateSetListener() {
        @Override
        public void onDateSet(DatePicker view, int year, int monthOfYear,
                              int dayOfMonth) {
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.YEAR, year);
            cal.set(Calendar.MONTH, monthOfYear);
            cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
            model.setDateTime(sdf.format(cal.getTime()));
            notifyDataSetChanged();
        }
    };

    Calendar cal = Calendar.getInstance();
    cal.setTime(sdf.parse(model.getDateTime()));
    new DatePickerDialog(this.activity, date, cal.get(Calendar.YEAR),
            cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
}

static class ViewHolder {
    EditText expenseName;
    EditText dateTime;
    EditText amount;
    ImageView editDate;
    ImageView deleteExpense;
    TextView number;

}

}


person Robin Rex    schedule 02.01.2015    source источник
comment
Это потому, что ListView повторно использует представления, а это означает, что каждый раз, когда convertview не равен нулю, он уже будет прикреплен TextWatcher к вашему EditText.   -  person Tomislav Novoselec    schedule 02.01.2015
comment
Попробуйте удалить TextWatcher перед viewHolder.amount.setText , это также вызовет этот обратный вызов. Используйте некоторую логику для использования одного экземпляра TextWatcher.   -  person Krish    schedule 02.01.2015
comment
Спасибо @TomislavNovoselec за ваш ответ.   -  person Robin Rex    schedule 05.01.2015
comment
Спасибо @Krish за ответ.   -  person Robin Rex    schedule 05.01.2015


Ответы (4)


Я знаю, что опаздываю, но я оставлю это здесь для будущих читателей. Немного сложно установить элементы с textwatchers на listview из-за их характера повторного использования, а также из-за того, что Textwatchers добавляются в текст редактирования, а не заменяются. Поэтому вам нужно удалять неправильные и добавлять новых наблюдателей каждый раз, когда создается представление строки или привязывается к новым данным. Попробуйте следующую реализацию:

  1. Пользовательский наблюдатель за текстом. (Элемент объекта — это объект, который содержит ваши данные строки)

    частный класс CustomWatcher реализует TextWatcher { элемент частного объекта;

     private CustomWatcher(Object item)
     {
         this.item = item;
     }
    
     @Override
     public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)
     {
    
     }
    
     @Override
     public void onTextChanged(CharSequence charSequence, int i, int i1, int i2)
     {
    
     }
    
     @Override
     public void afterTextChanged(Editable editable)
     {
    
     }
    

    }

  2. Добавьте textwatcher к вашему edittext и удалите предыдущий внутри вашей функции getView().

    CustomWatcher oldWatcher = (CustomWatcher)holder.editDate.getTag(); if(oldWatcher != null)holder.editDate.removeTextChangedListener(oldWatcher);

    // заполните ваш editText данными модели здесь (перед добавлением нового текстового наблюдателя)

    CustomWatcher newWatcher = новый CustomWatcher(rowItem); держатель.editDate.setTag (новый наблюдатель); держатель.editDate.addTextChangedListener (новый наблюдатель);

Сделанный. Теперь вы можете сохранить текст в соответствующем объекте данных строки. Таким образом, ваши Textwatchers будут удалены, когда они изживут свою полезность, а также предотвратят утечку памяти.

person ravindu1024    schedule 08.12.2016
comment
Идеальный. @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { // при необходимости измените модель. } - person bluetoothfx; 12.09.2018

Это из-за Text Watcher в вашем тексте редактирования, каждый раз, когда вы вводите текстовый наблюдатель, будет вызываться наблюдатель, и каждый раз, когда ваш notifyDataSetChanged() будет аннулирован.

Например: допустим, вы хотите сохранить сумму 453, она будет запущена 3 раза.

person Shankar    schedule 02.01.2015
comment
Спасибо за ответ. - person Robin Rex; 05.01.2015

Я пытался исправить это для одной из моих проблем, не в состоянии получить позицию, когда listview edittext имеет textWatcher.

Это решение, которое сработало для меня:

Внутри getview()

когда я добавляю слушателя textWatcher к editText, я также добавляю следующую строку:

edittext.setTag(R.id.position<any unique string identitiy>, position);

в вашем afterTextChanged() :

int position = edittext.getTag(R.id.position);

Это дает правильное положение, и вы можете вносить изменения в зависимости от положения.

person Sreeram Sunkara    schedule 26.04.2017

В Kotlin вы можете решить эту проблему, используя следующие методы расширения.

fun EditText.onChange(cb: (str: String) -> Unit){
    this.removeWatcher()
    val watcher = object : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

        override fun afterTextChanged(s: Editable?) {
            cb(s.toString())
        }
    }
    this.addTextChangedListener(watcher)
    this.tag = watcher
}

fun EditText.removeWatcher() {
    if(this.tag as TextWatcher? != null) {
        this.removeTextChangedListener(this.tag as TextWatcher)
    }
}

Используйте его из адаптера, например

yourEditText.onChange { str ->     // str is the new content
    // TODO: Implement listener here
}

Метод removeWatcher можно использовать всякий раз, когда вам нужно удалить всех наблюдателей из EditText.

Например: если вы установили некоторый текст в свой EditText из адаптера с помощью setText, будет вызван уже добавленный наблюдатель. В этом случае, если вы удалите все существующие наблюдатели до setText, можно избежать нежелательного вызова наблюдателей.

person shafeeq    schedule 27.06.2021