我有一个带有自定义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在创建时被填充(这里没有任何错误):
现在当我向下滚动时,会发生以下情况(仍然没有错):
当我向上滚动时,问题出现了:
我确实尝试在getView开始时暂时禁用TextWatcher(在我获得EditTexts之后),然后从OrderedProductItem设置值,然后重新应用MyTextWatcher,但之后它仍然会更改OrderedProductItem的值我重新应用了我的TextWatcher ..
使用TextWatcher保存Item的EditTexts的UserInput时,是否有人知道如何处理getView回收?
PS:我知道我可以在EditTexts旁边添加一个按钮,这样它只会在点击按钮时保存数据,但我不想这样。
答案 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);
这看似递归,如果您的视图持有者出现问题,可能会不同步。