我有包含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;
}
答案 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 - 屏幕上的那些,以及当用户下一个滚动时可能还有一两个用于回收。
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
开头的对我来说已经足够了。不确定解决方案的效率如何,但这正是我所寻找的。 p>
重要编辑:这是正常的当且仅当你的行数很少(没有滚动),否则这个黑客很可怕。您应该手动处理数据
答案 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);
}
}