I have list item with EditText in it, I dont know how many items there will be. I have a problem when I enter some text in EditText, and then scroll down a ListView, after I've scroll up again there is no text in my first EditText, or there is some text from other EditText from ListView.
I've tried TextWatcher, and saving data to array, but problems is that returned position of view in ListView isnt always right, so I lost some data from array. -.-
How to detect correct position of view in ListView?
For example:
If I have 10 items in ListView, and only 5 of them are currently visible. Adapter return position from 0 to 4...thats ok. When I scroll down position of item 6 is 0...wtf? and i lose data from array on position 0 :)
Im using ArrayAdapter.
Please help.
Here's some code:
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;
}
I had a related issue that I solved. Each row of my ListView
has a different View (it contains different controls. EditView, ImageView, TextView). I want the data entered or selected in the those controls to persist even when the control is scrolled off the screen. The solution I used was to implement the following methods in ArrayAdapter
like this:
public int getViewTypeCount() {
return getCount();
}
public int getItemViewType(int position) {
return position;
}
This will make sure that when getView()
gets called it passes the same View
instance that was returned from getView()
on the first call.
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView != null)
return convertView;
else
// create new view
}
you should try in this way as below:
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;
}
Explanation: Recycling behavior of ListView, actually erase all data bind with its Row Item, when go out of vision. So every new row coming in in vision from whatever direction you need to bind data again. Also, when EditText text change you have to keep values also in some data structure, so later on again row data binding you can extract data for position. You can use, array ArrayList for keeping edittext data, and also can use to bind data.
Similar behavior use in Recyclerview Adapter, behind logic you need to know about data binding of row data of adapters.
you can use setOnFocusChangeListener instead. in my case i have expandable List and it seems good :) like this :
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());
}
}
});
If you will only have ~10 rows, don't bother with the ListView
. Just put them in a vertical LinearLayout
and wrap that in a ScrollView
, and it will save you some headache.
If you are going to have dozens or hundreds of rows, I suggest that you come up with a better UX paradigm than EditText
widgets in ListView
rows.
All that being said, it feels like you are not handling your row recycling properly, or are unaware that rows get recycled. If you have 10 items in your ListAdapter
, and you only have room to display 5 rows with EditText
widgets, you should not wind up with 10 EditText
widgets when the user scrolls to the bottom. You should wind up with 5-7 -- the ones on the screen, and perhaps another one or two for recycling when the user scrolls next.
This free excerpt from one of my books goes through the process of creating custom subclasses of ArrayAdapter
and getting the recycling working. It also covers having an interactive row, using a RatingBar
for user input. That is far easier than an EditText
, because all you have to worry about are click events. You are welcome to try to expand upon that technique with EditText
widgets and TextWatcher
listeners, but I'm not a fan.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With