自定义 ListView 适配器。 TextChangedListener 调用错误的 EditText

Posted

技术标签:

【中文标题】自定义 ListView 适配器。 TextChangedListener 调用错误的 EditText【英文标题】:Custom ListView adapter. TextChangedListener calls for wrong EditText 【发布时间】:2012-09-22 03:39:27 【问题描述】:

我有带有自定义适配器的旅行者列表,其中包含两个 EditText - edtFirstName 和 edtLastName。我想要当用户输入文本时保存更改到列表,并且当下一个按钮单击时将此列表发送到另一个活动。

我的代码:

public class TravellersAdapter extends BaseAdapter 

  private List<Traveler> itemsList;
  private LayoutInflater inflater;
  private Activity context;

  public TravellersAdapter(Activity context, List<Traveler> itemsList) 
    super();
    this.itemsList = itemsList;
    this.context = context;
    inflater = LayoutInflater.from(context);
  

  public int getCount()  return itemsList.size();  

  public Object getItem(int i)   return itemsList.get(i);  

  public View getView(final int position, View view, ViewGroup viewGroup) 
    if (view == null) 
      view = inflater.inflate(R.layout.traveller_item, null);
    
    Traveler currentItem = (Traveler) getItem(position);

    EditText firstNameView = (EditText) view.findViewById(R.id.edtFirstName);
    firstNameView.addTextChangedListener(new TextWatcher() 

      public void afterTextChanged(Editable editable) 
        currentItem.setFirstName(editable.toString());
      

    );

    return view;
  

例如 List itemsList 包含 5 个项目。当我编辑 2-4 个元素时一切正常,但是当我编辑第一个或最后一个元素时,编辑值分配给列表中的所有元素。在 dubugger 中,我看到该方法 afterTextChanged 以不同的 position 值调用了 5 次。 如何解决?

【问题讨论】:

您的 firstNameView 被回收用于多个项目。你的 currentItem 也是如此。 【参考方案1】:

getView方法中,参数position给出的是新创建的childView的位置,而不是被点击的childView的位置。

使用它来获得正确的位置:

final int actual_position = myList.getPositionForView((View) v.getParent());

在任何视图的onClickListeneronClick(View v); 中。在您的情况下,您必须为该 EditText 实现 onTextChangedListener

这里:

myList 是 ListView

v 是您点击的视图,在本例中是父视图(myList)的子视图。

【讨论】:

【参考方案2】:

之所以会出现此问题,是因为视图是可重用的(这是 android API 的设计)。所以最终你可以为同一个文本视图分配超过 1 个文本观察者。当文本视图中的文本发生更改时,所有分配的观察者都会被触发。

一个快速解决方法(如果列表真的很长,比如 1000 多个项目,则不是最佳解决方案)是拥有Traweller -&gt; TextWatcher 的映射。

然后在 getView() 内部你可以这样做(伪代码):

查看地图是否有TextWatcher 对应这个Traweller 如果map没有,则新建一个TextWatcher,放入map并赋值给EditText 否则将 TextWatcherEditText 中分离出来并从地图中删除 新建一个TextWatcher,放入地图并分配给EditText

【讨论】:

【参考方案3】:

在不可见的屏幕中再创建一个EditText,名称为invivisbleEt。 并在addTextChangedListener中执行以下操作

firstNameView.addTextChangedListener(new TextWatcher() 

    @Override
    public void afterTextChanged(Editable editable) 
        if(!firstNameView.isFocused())
            currentItem.setFirstName(editable.toString());
    

);

同样在onCreate 方法中为ListView 对象添加此代码。

lv.setOnScrollListener(new AbsListView.OnScrollListener() 

    //public boolean scrolling;

    @Override
    public void onScrollStateChanged(AbsListView absListView, int scrollState) 
        invivisbleEt.requestFocus();
    

    @Override
    public void onScroll(AbsListView absListView, int i, int i1, int i2) 
    
);

【讨论】:

以上是关于自定义 ListView 适配器。 TextChangedListener 调用错误的 EditText的主要内容,如果未能解决你的问题,请参考以下文章

使用自定义(对象)适配器过滤 ListView

具有包含 CheckBoxes 的自定义适配器的 Listview

滚动时 ListView 自定义适配器错误

自定义 ListView 适配器中的 ImageButton

ListView 自定义适配器,包括来自画廊的图像

使用自定义适配器自定义 Android ListView