带有 edittext 和 textwatcher 的列表视图。 Textwatcher 触发多次(超过 3 次)

Listview with edittext and textwatcher. Textwatcher fires multiple times (more than 3 times)

我有一个自定义列表适配器,其中一个列表项具有三个编辑文本。我有一个日期选择器供其中之一使用。设置日期后,适配器的相应型号会正确保存日期。但是对于其他编辑文本,我只想将输入的文本存储在列表中。我使用 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;

}

}

因为你的Edit Text上有Text Watcher,每次你输入字符时,text watcher都会被调用,每次你的notifyDataSetChanged()都会被撤销。

例如:假设您要保存金额453,它将被触发3次。

我知道我来晚了,但我会把它留给未来的读者。将 textwatchers 设置为 listview 项目有点棘手,因为它们的回收性质以及 Textwatchers 被添加到编辑文本而不是被替换。因此,每次创建行视图或绑定到新数据时,您都需要删除不正确的并添加新的观察者。尝试以下实施:

  1. 自定义文本观察器。 (对象项是包含您的行数据的对象)

    私有 class 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(); 如果(旧观察者!= null) holder.editDate.removeTextChangedListener(旧观察者);

    //在此处使用模型数据填充您的 editText(在添加新的文本观察器之前)

    CustomWatcher newWatcher = new CustomWatcher(rowItem); holder.editDate.setTag(newWatcher); holder.editDate.addTextChangedListener(newWatcher);

完成。您现在可以将文本保存在相应的行数据对象中。这样,您的 Textwatchers 将在它们不再有用时被删除,并防止内存泄漏。

我试图解决我的一个问题,当 listviewedittexttextWatcher.

时无法获得位置

这是对我有用的解决方案:

里面getview()

当我向 editText 添加 textWatcher 侦听器时,我还添加了以下行:

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

在你的 afterTextChanged() 中:

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

它给出了正确的位置,您可以根据位置进行修改。

在 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
}

只要您需要从 EditText.

中删除所有观察者,就可以使用 removeWatcher 方法

例如:如果您使用 setText 从适配器在 EditText 中设置一些文本,将调用已添加的观察器。在这种情况下,如果您在 setText 之前删除所有现有观察者,则可以避免不必要的观察者调用