1

我有一个带有自定义 ArrayAdapter 的 ListView,它使用OrderedProductItems(我自己的模型类)列表。在这个 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 中,我将用户的输入保存到我的 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(有关 getView 回收(包括图片)的更多信息,请参阅这篇文章),我的 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”

现在当我向下滚动时,会发生以下情况(仍然没有错):

  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”

当我向上滚动时,问题出现了:

  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”

我确实尝试在 getView 开始时暂时禁用 TextWatcher(在我获得 EditTexts 之后),然后从 OrderedProductItem 设置值,然后将 MyTextWatcher 重新应用到它们,但是在我重新之后它仍然会更改 OrderedProductItem 的值-应用了我的 TextWatcher ..

有谁知道在使用 TextWatcher 保存 Item 的 EditTexts 的 UserInput 时如何处理 getView 回收?

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

4

1 回答 1

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);

这看起来是递归的,如果您的视图持有者有问题,可能会不同步。

于 2014-08-11T12:00:26.303 回答