EditText在ListView中滚动时丢失内容

时间:2012-01-07 20:11:37

标签: android listview android-edittext

我有包含EditText的列表项,我不知道会有多少项。 我在EditText中输入一些文本,然后向下滚动ListView后出现问题,在我再次向上滚动后,我的第一个EditText中没有文本,或者ListView中有来自其他EditText的文本。

我已经尝试过TextWatcher,并将数据保存到数组中,但问题是ListView中返回的视图位置并不总是正确的,所以我从数组中丢失了一些数据。 -.-

如何在ListView中检测正确的视图位置?

例如:

如果我在ListView中有10个项目,目前只有5个项目可见。 适配器返回位置从0到4 ......没关系。 当我向下滚动项目6的位置是0 ... wtf?并且我从位置0的数组中丢失数据:)

我正在使用ArrayAdapter。

请帮忙。

以下是一些代码:

public View getView(int position, View convertView, ViewGroup parent) {

    tmp_position = position;

    if (convertView == null) {

        holder = new ViewHolder();

        LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = vi.inflate(R.layout.element_in_game, null);

        holder.scoreToUpdate = (EditText) convertView
                .findViewById(R.id.elementUpdateScore);

        holder.scoreToUpdate.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start,
                    int before, int count) {
                scoresToUpdate[tmp_position] = s.toString();
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start,
                    int count, int after) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        initScoresToUpdateEditTexts(holder.scoreToUpdate, hint);

        convertView.setTag(holder);

    } else {

        holder = (ViewHolder) convertView.getTag();

        holder.scoreToUpdate.setText(scoresToUpdate[tmp_position]);

    }

    return convertView;
}

11 个答案:

答案 0 :(得分:16)

您应该尝试以下方式:

if (convertView == null) {

        holder = new ViewHolder();

        LayoutInflater vi = (LayoutInflater)    
        getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = vi.inflate(R.layout.element_in_game, null);

        holder.scoreToUpdate = (EditText) convertView
                .findViewById(R.id.elementUpdateScore);


        convertView.setTag(holder);

    } else {

        holder = (ViewHolder) convertView.getTag();

    }
   //binding data from array list
   holder.scoreToUpdate.setText(scoresToUpdate[position]);
   holder.scoreToUpdate.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start,
                    int before, int count) {
                //setting data to array, when changed
                scoresToUpdate[position] = s.toString();
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start,
                    int count, int after) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

    return convertView;
}

说明: ListView的回收行为,实际上在超出视觉范围时将其所有数据与其Row Item绑定。 因此,每个新行都会从您需要再次绑定数据的任何方向进入视觉。 此外,当EditText文本更改时,您还必须在某些数据结构中保留值,因此稍后再次对行数据进行绑定,您可以提取位置数据。 您可以使用数组ArrayList来保存edittext数据,也可以用来绑定数据。

类似的行为在Recyclerview Adapter中使用,在逻辑背后需要了解适配器行数据的数据绑定。

答案 1 :(得分:12)

您可以使用setOnFocusChangeListener。 在我的情况下,我有可扩展的列表,它似乎很好:) 像这样:

holder.count.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        public void onFocusChange(View v, boolean hasFocus) {
            if (!hasFocus) {
                EditText et =(EditText)v.findViewById(R.id.txtCount);
                myList.get(parentPos).put(childPos,
                        et.getText().toString().trim());
            }
        }
    });

答案 2 :(得分:5)

如果您只有~10行,请不要理会ListView。只需将它们放在一个垂直的LinearLayout中并将其包裹在ScrollView中,这样可以省去一些麻烦。

如果您要拥有数十行或数百行,我建议您提出比EditText行中的ListView小部件更好的UX范例。

所有这一切,感觉就像你没有正确处理你的行回收,或者不知道行被回收。如果您的ListAdapter中有10个项目,并且只有显示5行EditText小部件的空间,那么当用户滚动到底部时,您不应该使用10个EditText小部件。您应该使用5-7 - 屏幕上的那些,以及当用户下一个滚动时可能还有一两个用于回收。

来自This free excerpt

one of my books经历了创建ArrayAdapter的自定义子类并使回收工作正常的过程。它还包括使用RatingBar进行用户输入的交互式行。这比EditText容易得多,因为您只需点击事件即可。欢迎您尝试使用EditText小部件和TextWatcher听众扩展该技术,但我不是粉丝。

答案 3 :(得分:4)

我遇到了一个与之相关的问题。我的ListView的每一行都有不同的视图(它包含不同的控件.RedView,ImageView,TextView)。我希望即使控件滚出屏幕,在这些控件中输入或选择的数据也会保持不变。我使用的解决方案是在ArrayAdapter中实现以下方法:

public int getViewTypeCount() {
    return getCount();
}

public int getItemViewType(int position) {
    return position;
}

这将确保在调用getView()时它会传递第一次调用时从View返回的相同getView()实例。

public View getView(int position, View convertView, ViewGroup parent) {

  if (convertView != null)
    return convertView;
  else
  // create new view
}

答案 4 :(得分:2)

我刚刚寻找类似的解决方案,发现使用textChangeListener并不是最好的方法。我在这里回答了一个相关的问题:

https://stackoverflow.com/a/13312282/1812518

这是我的第一篇文章,但我希望它足够有用。

答案 5 :(得分:1)

从您的代码的简短浏览中,看起来您正在与回收商对接。当您从列表视图中的位置1滚动到位置20时,它不会创建列表视图项目视图的20个不同实例。相反它有7或8-当一个滚动屏幕时,它被添加到回收器,然后作为convertView参数发送到getView方法,因此您可以使用新数据重新填充它而不是浪费地创建新视图

传统上使用ViewHolder模式来保存列表项(textview,imageview等)的子视图,而不是数据(设置并从持有者中获取分数) - 发生的事情是您将值设置为零位置零,向下滚动到5项屏幕上的第6项,第0项重新用于位置6,它从附加到该列表项的持有者中拉出现有值(在这种情况下,为0)

简单回答:不要在持有人中存储特定位置的数据!将它存放在外部数组或哈希表中。

答案 6 :(得分:0)

更好的方法是在运行时创建EditText并将其id设置为位置参数

getView()方法的

。所以现在添加文本时,将其存储在 Vector 变量(类

)中

变量),并在基于位置变量的getView方法中设置Text(你可以

使用相应EditText的elementAt() vector方法)。

并且不要忘记将此EditText添加到膨胀的视图中。

答案 7 :(得分:0)

本准则将为您提供帮助

private class EfficientAdapter extends BaseAdapter {
        private LayoutInflater mInflater;
        private String[] attitude_names;
        public String[] attitude_values;
        private String name;
public static HashMap<Integer,String> myList=new HashMap<Integer,String>();

        public EfficientAdapter(Context context) {
            mInflater = LayoutInflater.from(context);
            attitude_names = context.getResources().getStringArray(R.array.COMP_ATTITUDE_NAME);
            attitude_values = new String[attitude_names.length];
        }
// initialize myList
for(int i=0;i<attitude_names.length;i++)
{
   myList.put(i,"");
}


        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            final ViewHolder holder;

            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.addcomp_attitude_row, null);

                holder = new ViewHolder();
                holder.Attitude_Name = (TextView) convertView.findViewById(R.id.addcomp_att_name);
                holder.Attitude_Value = (EditText) convertView.findViewById(R.id.addcomp_att_value);
                holder.Attitude_Value.addTextChangedListener(new TextWatcher()
                    {
                        public void afterTextChanged(Editable edt) 
                        {
                             myList.put(pos,s.toString.trim());
                            attitude_values[holder.ref] = edt.toString();
                        }

                        public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}

                        public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
                            //attitude_values[ref] = Attitude_Value.getText().toString();
                        }
                    });

                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }


            holder.ref=position;
            holder.Attitude_Name.setText(attitude_names[position]);
            holder.Attitude_Value.setHint(attitude_names[position]);
            holder.Attitude_Value.setText(myList.get(position));




            return convertView;
        }

        class ViewHolder {
            TextView Attitude_Name;
            EditText Attitude_Value; 
            int ref;



        }

        @Override
        public int getCount() {
            return attitude_names.length;
        }
    }

这里我已经包含了一个HashMap对象,它将继续关注EditText包含的值。当你滚动listview时,它将通过调用它的getView方法再次呈现。

在这段代码中,当你第一次加载listview时,你所有的edittext都没有text.once你输入一些文本,它会在myList中注明。所以当你再次渲染列表时,你的文本就会被阻止。< / p>

答案 8 :(得分:0)

获取convertView == null支票及其他声明。你的性能会变差,但会导致回收无效。

答案 9 :(得分:0)

我的适配器扩展了BaseAdapter,我有类似bickster的情况,我有列表视图,其中有不同的视图(动态形式的情况),需要持久化数据。我的列表视图不会太大,所以在我的情况下回收视图不会是一个很大的资源,因此很简单

if (convertView != null) return convertView;

getView开头的

对我来说已经足够了。不确定解决方案的效率如何,但这正是我所寻找的。

重要编辑:这是正常的当且仅当你的行数很少(没有滚动),否则这个黑客很可怕。您应该手动处理数据

答案 10 :(得分:0)

首先,声明一个String[]List<String>成员变量(在getView() inside 类中, inside ),以存储输入EditText对象中的文本,并将文本记录在您在addTextChangeListener()对象上设置的EditText中。

    viewHolder.editText.removeTextChangedListener(viewHolder.textWatcher);

    viewHolder.textWatcher = new TextWatcher() {
        @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) {
            mInputs[position] = editable.toString();            //record input

            viewHolder.editText.setSelection(viewHolder.editText.getText().length());       //set cursor to the end
        }
    };

    viewHolder.editText.addTextChangedListener(viewHolder.textWatcher);
    viewHolder.editText.setText(mInputs[position]);

private class ViewHolder {
    EditText editText;
    TextWatcher textWatcher;

    public ViewHolder(View itemView) {
        editText = itemView.findViewById(R.id.main_editText);
    }
}