ListViewText中带有自定义ArrayAdapter(和getView回收)的EditText的TextWatcher

时间:2014-08-11 11:39:05

标签: android android-listview android-arrayadapter textwatcher android-viewholder

我有一个带有自定义ArrayAdapter的ListView,它使用OrderedProductItems(我自己的Model-class)列表。在这个ArrayAdapter的getView中,我使用ViewHolder设计模式:

@Override
public View getView(int position, View convertView, ViewGroup parent){
    View view = convertView;
    MyHolder h;
    if(view == null){
        view = inflater.inflate(layoutResourceId, parent, false);
        RelativeLayout rl = (RelativeLayout) view.findViewById(R.id.item_layout);
        h = new MyHolder(rl);
        view.setTag(h);
    }
    else
        h = (MyHolder) view.getTag();

    if(h != null){
        h.orderedProductItem = getItem(position);

        ... // Do stuff like:
            //    - setting Texts of TextViews/EditText
            //    - updating data of AutoCompleteTextView and Spinner
            //    - etc.

        h.etResultPrice.setTag(h.orderedProductItem);
        h.etResultPrice.addTextChangedListener(new MyTextWatcher(h.etResultPrice));
    }
}

在我的ListView的项目中,我有一个使用自定义TextWatcher的EditText。在这个TextWatcher中,我将User的输入保存到我的OrderedProductItem类:

public class MyTextWatcher implements TextWatcher
{
    // Logcat tag
    private static final String TAG = "MyTextWatcher";

    // The values used for the test output of changed EditText texts
    private View view;
    private String from;

    public MyTextWatcher(View v){
        view = v;
    }

    // Default Android method used by TextWatcher
    // (to do something before an EditText's is going to change)
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Get the old text before we make a change to this EditText
        from = ((EditText)view).getText().toString();
    }

    // Default Android method used by TextWatcher
    // (to do something when an EditText's text is changing)
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {}

    // Default Android method used by TextWatcher
    // (to do something after an EditText's text is changed)
    @Override
    public void afterTextChanged(Editable s) {
        if(s != null && s.toString() != null && view.getTag() != null){
            OrderedProductItem opi = (OrderedProductItem)view.getTag();
            if(opi != null){
                // Send the changed text of this EditText to the OrderedProductItem's Result-values
                if(view.getId() == R.id.et_result_amount){
                    int amount = 1;
                    try{
                        amount = Integer.parseInt(s.toString());
                    }
                    catch(NumberFormatException ex){
                        amount = 1;
                    }
                    if(opi.getResultAmount() != amount){
                        opi.setResultAmount(amount);
                        Log.i(TAG, "ResultAmount changed from " + from + " to " + s.toString() + " (-> " + String.valueOf(opi.getResultAmount()) + ")");
                    }
                }
                else if(view.getId() == R.id.actv_result_name){
                    if(!V.compare(opi.getResultName(), s.toString(), false, false)){
                        opi.setResultName(s.toString());
                        Log.i(TAG, "ResultName changed from " + from + " to " + s.toString() + " (-> " + opi.getResultName() + ")");
                    }
                }
                else if(view.getId() == R.id.et_result_price){
                    double price = C.priceStringToDouble(s.toString());
                    if(opi.getResultPrice() != price){
                        opi.setResultPrice(price);
                        Log.i(TAG, "ResultPrice changed from " + from + " to " + s.toString() + " (-> " + String.valueOf(opi.getResultPrice()) + ")");
                    }
                }
            }
            else
                Log.wtf(TAG, "Tag's OrderedProductItem is null");
        }
    }
}

现在我的问题是:由于ArrayAdapter使用getView-recycler(see this post for more information about getView recycling (including picture)),我的TextWatcher会覆盖另一个OrderedProductItem的错误数据。例如,假设我的OrderedProductItem的EditTexts在创建时被填充(这里没有任何错误):

  1. 预期“1” - 实际“1”
  2. 预期“2” - 实际“2”
  3. 预期“3” - 实际“3”
  4. 预期“4” - 实际“4”
  5. 预期“5” - 实际“5”
  6. 预期“6” - 实际“6”
  7. 预期“7” - 实际“7”
  8. 预期“8” - 实际“8”
  9. 预期“9” - 实际“9”
  10. 预期“10” - 实际“10”
  11. 现在当我向下滚动时,会发生以下情况(仍然没有错):

    1. 预期“5” - 实际“5”
    2. 预期“6” - 实际“6”
    3. 预期“7” - 实际“7”
    4. 预期“8” - 实际“8”
    5. 预期“9” - 实际“9”
    6. 预期“10” - 实际“10”
    7. 预期“11” - 实际“11”
    8. 预期“12” - 实际“12”
    9. 预期“13” - 实际“13”
    10. 预计“14” - 实际“14”
    11. 当我向上滚动时,问题出现了:

      1. 预期“1” - 实际“14”
      2. 预期“2” - 实际“13”
      3. 预期“3” - 实际“12”
      4. 预期“4” - 实际“11”
      5. 预期“5” - 实际“5”
      6. 预期“6” - 实际“6”
      7. 预期“7” - 实际“7”
      8. 预期“8” - 实际“8”
      9. 预期“9” - 实际“9”
      10. 预期“10” - 实际“10”
      11. 我确实尝试在getView开始时暂时禁用TextWatcher(在我获得EditTexts之后),然后从OrderedProductItem设置值,然后重新应用MyTextWatcher,但之后它仍然会更改OrderedProductItem的值我重新应用了我的TextWatcher ..

        使用TextWatcher保存Item的EditTexts的UserInput时,是否有人知道如何处理getView回收?

        PS:我知道我可以在EditTexts旁边添加一个按钮,这样它只会在点击按钮时保存数据,但我不想这样。

1 个答案:

答案 0 :(得分:3)

看起来你的问题就在这里:

 etResultPrice.setTag(currentOrderedProductItem);

如果您尝试删除TextWatcher但仍有问题,它会引用标记,如果您没有使用循环视图正确重置标记,则会出现问题。特别是,看起来你正在尝试重置它,但没有正确地执行它,因为当视图被回收时,它们应该按顺序排列。这意味着当你向后滚动时,你的行:

 expected "3" -- actual "12"
 expected "4" -- actual "11"
 expected "5" -- actual "5"
 expected "6" -- actual "6"

关于“预期”4' - 实际“11”“ - 你重置标签的逻辑必须是错误的,因为它应该是回收”14“ - 所以这不是回收问题,这是你的逻辑用来执行回收。你的代码假设这是第11项,就像在初始滚动中一样,而不是认识到它应该是#4。

可能在某处:创建视图时,您可以这样做:

 view.setTag(h);

但在你的逻辑中,你也这样做:

h.etResultPrice.setTag(h.orderedProductItem);

这看似递归,如果您的视图持有者出现问题,可能会不同步。