Java – Why does my ListView reorder as I scroll?

Why does my ListView reorder as I scroll?… here is a solution to the problem.

Why does my ListView reorder as I scroll?

When I scroll through, my ListView reordered. This is very confusing.

This is the custom adapter I’m using :

    public class LoadExpenseList extends BaseAdapter{
        List<Expense> expenses;
        Context context;

public LoadExpenseList(Context context, int textViewResourceId,
                List<Expense> expenses) {
            super();
            this.expenses = expenses;
            this.context = context;
        }

public View getView(final int position, View convertView, ViewGroup parent){
            View v = convertView;
            AvailableExpenseView btv;

if (convertView == null) {
                btv = new AvailableExpenseView(context, expenses.get(position));
            } else {
                btv = (AvailableExpenseView) convertView;
            }           
            btv.setOnClickListener(new OnClickListener() {

@Override
                public void onClick(View v) {
                    Log.i("Expense_Availables", "Item Selected!!");
                    Intent intent = new Intent(getActivity(), ItemDetailActivity.class);

int id = expenses.get(position).getExpenseItemId();
                    intent.putExtra("id", id);

startActivity(intent);
                }

});

btv.setOnLongClickListener(new OnLongClickListener() {

@Override
                public boolean onLongClick(View arg0) {
                     TODO Auto-generated method stub
                    return false;
                }

});

registerForContextMenu(btv);

return btv;
        }

@Override
        public int getCount() {
            return expenses.size();
        }

@Override
        public Object getItem(int position) {
            return expenses.get(position);
        }

@Override
        public long getItemId(int position) {
            return expenses.get(position).getExpenseItemId();
        }

}

Solution

Because your View (AvailableExpenseView) is constructed from a single project, when the adapter tries to reuse the View through a convertView, you get a view that has been binded to another project.

Instead of building a View with model items, call a method like convertView.setExpense(expenses.get(position)).

The ListView attempts to reuse the View to improve performance. So what happens is that the first item in the list shows the newly created View, and later when you scroll, it will try to reuse the previously created View to give you the View through the convertView. Note the lines:

        if (convertView == null) {
             You create a view using the proper item
            btv = new AvailableExpenseView(context, expenses.get(position)); 
        } else {
             You don't override the item that was previously assigned 
             when the view was created
            btv = (AvailableExpenseView) convertView;
        }    

If the convertView is null, you are creating a new View, but you are building your View using a project. So, suppose it is called on position 0. You create a View with the first expense in the list. Later, listView wants to get the View at location 20 and says “Okay, let’s reuse the View we used for position 0”, so it passes this View as a convertView, but this View has already been created with the project at location 0, you don’t overwrite it. So you end up using a View where the first item represents the 20th item.

To solve this problem, you can easily do the following:

        AvailableExpenseView btv;

if (convertView == null) {
             dont create your view with an item
            btv = new AvailableExpenseView(context);
        } else {
            btv = (AvailableExpenseView) convertView;
        }

 Assign the expense wether it is a newly created view or
         a view that is reused
        btv.setExpense(expenses.get(position));

Of course, you must edit the AvailableExpenseView and create a setExpense() method to populate your View.

Related Problems and Solutions