Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListView in ArrayAdapter order get's mixed up when scrolling

I have a ListView in a custom ArrayAdapter that displays an icon ImageView and a TextView in each row. When I make the list long enough to let you scroll through it, the order starts out right, but when I start to scroll down, some of the earlier entries start re-appearing. If I scroll back up, the old order changes. Doing this repeatedly eventually causes the entire list order to be seemingly random. So scrolling the list is either causing the child order to change, or the drawing is not refreshing correctly.

What could cause something like this to happen? I need the order the items are displayed to the user to be the same order they are added to the ArrayList, or at LEAST to remain in one static order. If I need to provide more detailed information, please let me know. Any help is appreciated. Thanks.

like image 735
Dan B Avatar asked Jun 02 '10 05:06

Dan B


People also ask

How do I make ListView scroll smoothly?

The key to a smoothly scrolling ListView is to keep the application's main thread (the UI thread) free from heavy processing. Ensure you do any disk access, network access, or SQL access in a separate thread. To test the status of your app, you can enable StrictMode .

Is list view scrollable by default?

ListView itself is scrollable.

Is ArrayAdapter used to handle ArrayList?

The simplest adapter to use is called an ArrayAdapter because the adapter converts an ArrayList of objects into View items loaded into the ListView container.


4 Answers

I was having similar issues, but when clicking an item in the custom list, the items on the screen would reverse in sequence. If I clicked again, they'd reverse back to where they were originally.

After reading this, I checked my code where I overload the getView method. I was getting the view from the convertedView, and if it was null, that's when I'd build my stuff. However, after placing a breakpoint, I found that it was calling this method on every click and on subsequent clicks, the convertedView was not null therefore the items weren't being set.

Here is an example of what it was:


public View getView(int position, View convertView, ViewGroup parent)
{
    View view = convertView;
    if (view == null)
    {
        LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = vi.inflate(R.layout.listitemrow, null);

        RssItem rssItem = (RssItem) super.getItem(position);
        if (rssItem != null)
        {
            TextView title = (TextView) view.findViewById(R.id.rowtitle);
            if (title != null)
            {
                title.setText(rssItem.getTitle());
            }
        }
    }
    return view;
}

The subtle change is moving the close brace for the null check on the view to just after inflating:


public View getView(int position, View convertView, ViewGroup parent)
{
    View view = convertView;
    if (view == null)
    {
        LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = vi.inflate(R.layout.listitemrow, null);
    }
    RssItem rssItem = (RssItem) super.getItem(position);
    if (rssItem != null)
    {
        TextView title = (TextView) view.findViewById(R.id.rowtitle);
        if (title != null)
        {
            title.setText(rssItem.getTitle());
        }
    }
    return view;
}

I hope this helps others who experience this same problem.

like image 152
farcrats Avatar answered Oct 07 '22 23:10

farcrats


To further clarify the answer of farcats below in more general way, here is my explanation:

The vi.inflate operation (needed here for parsing of the layout of a row from XML and creating the appropriate View object) is wrapped by an if (view == null) statement for efficiency, so the inflation of the same object will not happen again and again every time it pops into view.

HOWEVER, the other parts of the getView method are used to set other parameters and therefore should NOT be included within the if (view == null) statement.

Similarily, in other common implementation of this method, some textView, ImageView or ImageButton elements need to be populated by values from the list[position], using findViewById and after that .setText or .setImageBitmap operations. These operations must come after both creating a view from scratch by inflation and getting an existing view if not null.

Another good example where this solution is applied for BaseAdapter appears in BaseAdapter causing ListView to go out of order when scrolled

like image 34
wiztrail Avatar answered Oct 07 '22 22:10

wiztrail


The ListView reuses view objects when you scroll. Are you overriding the getView method? You need to make sure you set each property for every view, don't assume that it will remember what you had before. If you post that method, someone can probably point you at the part that is incorrect.

like image 35
Cheryl Simon Avatar answered Oct 07 '22 23:10

Cheryl Simon


I have a ListView, AdapterView and a View (search_options) that contains EditText and 3 Spinners. ListView items are multiple copies of (search_options) layout, where user can add more options in ListView then click search to send sql query built according to users options.

I found that convertView mixing indecies so I added a global list (myViews) in activity and passed it to ArrayAdapter. Then in ArrayAdapter (getView) I add every newly added view to it (myViews).

Also on getView instead of checking if convertView is null, I check if the global list (myViews) has a view on the selected (position).. It totally solved problems after losing 3 days reading the internet!!

1- on Activity add this:

Map<Integer, View> myViews = new HashMap<>();

and then pass it to ArrayAdapter using adapter constructor.

mSOAdapter = new SearchOptionsAdapter(getActivity(), resultStrs, myViews);

2- on getView:

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

    View view;
    ViewHolder viewHolder;

    if (!myViews.containsKey(position)) {
        viewHolder = new ViewHolder();
        LayoutInflater inflater = LayoutInflater.from(getContext());
        view = inflater.inflate(R.layout.search_options, parent, false);

        /// ...... YOUR CODE

        myViews.put(position, view);

        FontUtils.setCustomFontsIn(view, getContext().getAssets());

    }else {
        view = myViews.get(position);
    }

    return view;
}

Finally no more mixing items...

like image 27
Ibrahim Khalil Avatar answered Oct 07 '22 22:10

Ibrahim Khalil