EditText loses content on scroll in ListView

2019-01-03 10:10发布

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;
}

10条回答
贼婆χ
2楼-- · 2019-01-03 10:24

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());
            }
        }
    });
查看更多
三岁会撩人
3楼-- · 2019-01-03 10:25

From a brief skim of your code, it looks like you're butting up against the recycler. When you scroll from position 1 to position 20 in a listview, it doesn't create 20 different instances of the listview item's view. Instead it has 7 or 8- When one scrolls off the screen, it gets added to the recycler, and then sent to the getView method as the convertView parameter, so you can re-populate it with new data instead of wastefully creating a new view.

Traditionally one uses the ViewHolder pattern to hold the subviews of a list item (textview, imageview, etc), not data (setting and getting the score from within the holder) - What's happening is that you set the value to zero at position zero, scroll down to the 6th item on a 5-item screen, the 0th item gets re-used for position 6, and it pulls the existing value from the holder attached to that listitem (in this case, a 0)

Simple answer: Don't store position-specific data in the holder! Stash it in an external array or hashtable somewhere.

查看更多
祖国的老花朵
4楼-- · 2019-01-03 10:25

This Code will help you

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;
        }
    }

Here I have included a HashMap object which will keep on eye of what EditText contains value.And when you scroll the listview,it will be rendered again by calling its getView method.

In this code,when you firstly load listview,all your edittext will be with no text.once you enter some text,it will be noted in myList.So when you again render the list,your text would be prevented.

查看更多
We Are One
5楼-- · 2019-01-03 10:25

My adapter extends the BaseAdapter and I had a similar situation as bickster had where I had list view which had different views in them (a dynamic form like situation) and needed to persist data. My list view was not going to be too big, so recycling the view was not going to be a big resource consume in my case and hence a simple

if (convertView != null) return convertView;

at the start of the getView was sufficient for me. Not sure how efficient a solution it is, but it was what I was looking for.

Important Edit: This is ok if and only if you have very few rows (no scrolling), otherwise this hack is horrible one. You should instead take care of the data manually

查看更多
兄弟一词,经得起流年.
6楼-- · 2019-01-03 10:27

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.

查看更多
ら.Afraid
7楼-- · 2019-01-03 10:27

Get ride of the convertView == null check and it's else statement. Your going to get worse performance but it'll disable the recycling.

查看更多
登录 后发表回答