【问题标题】:Listview with edittext and textwatcher. Textwatcher fires multiple times (more than 3 times)带有 edittext 和 textwatcher 的列表视图。 Textwatcher 触发多次(超过 3 次)
【发布时间】:2015-02-28 17:44:57
【问题描述】:

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

}

}

【问题讨论】:

  • 这是因为 ListView 回收了视图,这意味着每次 convertview 不为空时,它已经在你的 EditText 上附加了 TextWatcher。
  • 尝试在 viewHolder.amount.setText 之前移除 TextWatcher ,也会触发这个回调。使用一些逻辑来使用 TextWatcher 的单个实例。
  • 感谢@TomislavNovoselec 的回答。
  • 谢谢@Krish 的回答。

标签: android android-edittext textwatcher


【解决方案1】:

我知道我迟到了,但我会把这个留给未来的读者。将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 = new CustomWatcher(rowItem); holder.editDate.setTag(newWatcher); holder.editDate.addTextChangedListener(newWatcher);

完成。您现在可以将文本保存在相应的行数据对象中。这样一来,您的 Textwatcher 将在其失效时被移除,并防止内存泄漏。

【讨论】:

  • 完美。 @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { //根据需要更改模型。 }
【解决方案2】:

这是因为你的编辑文本上的文本观察器,每次你输入字符文本观察器都会被调用,每次你的 notifyDataSetChanged() 都会被撤销。

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

【讨论】:

    【解决方案3】:

    我试图解决我的一个问题,当listview 的edittext 有textWatcher 时无法获得该职位。

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

    内部getview()

    当我将textWatcher 监听器添加到editText 时,我还添加了以下行:

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

    在你的afterTextChanged():

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

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

    【讨论】:

      【解决方案4】:

      在 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 之前删除所有现有的观察者,则可以避免不必要地调用观察者

      【讨论】:

        猜你喜欢
        • 2013-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-16
        • 1970-01-01
        相关资源
        最近更新 更多