androidlistviewandroid-arrayadaptercustom-arrayadapter

ListViewAdapter shuffles items


So I have a listview pulling data from a Room based SQLite database. It is reading the data just fine it seems, but when the page loads I will get the first ~10 entries (the size of the display), and when I attempt to scroll down on the listview it starts back over and begins getting the entries at the beginning of the list again. The red circles on the screenshot demonstrate the top and bottom of the list and the repetition that is present.

public class NeighborhoodListAdapter
    extends ArrayAdapter<LivingCity> {

    private List<LivingCity> neighborhood;
    Context context;

    // View lookup cache
    private static class ViewHolder {
        TextView cityName;
    }

    public NeighborhoodListAdapter(List<LivingCity> neighborhood, Context context) {
        super(context, R.layout.object_main_citydetailsrow, neighborhood);
        this.context = context;
        this.neighborhood = neighborhood;
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Get the data item for this position
        LivingCity city = getItem(position);
        ViewHolder viewHolder; 

        if (convertView == null) {

            viewHolder = new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(getContext());
            convertView = inflater.inflate(R.layout.object_main_citydetailsrow, parent, false);
            viewHolder.cityUid  = (TextView) convertView.findViewById(R.id.cityrow_uid);
                viewHolder.cityUid.setText(String.format(Locale.ENGLISH,"#%d", city.getUid()));
                }//city.getUid() returns an int
            }catch (Exception e){
                Log.d("editCity: ", e.toString());
            }


            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        // Return the completed view to render on screen
        return convertView;
    }
}

and I am starting it with

void loadNeighborhood() {
    neighborhoodDB = Neighborhood.getAppDatabase(this);
    neighborhood = neighborhoodDB.getNeighborhood();
}

which pulls a sorted list from the database helper

public List<LivingCity> getNeighborhood() {
        List<LivingCity> neighborhood = cityDao().getAll();

        Collections.sort(neighborhood,
                new Comparator<LivingCity>() {


            @Override
            public int compare(LivingCity city1, LivingCity city2) {
                return Integer.valueOf(city1.getUid()).compareTo(city2.getUid());
            } //I thought this sort operation might be whats wrong but same error with it commented out
        });

        return neighborhood;
    }

Edit: Additionally, if I scroll to the bottom repeatedly without going up I can trick it into shuffling some of the upper items as shown in the screenshot (Orange).

Shuffling listview


Solution

  • When you scroll the ListView recycles old views. But you are only updating the value only when a new row is inflated. Inflation of a new row does not occur all the time, it reuses an old row. This this the reason you are seeing old values again. Move the line viewHolder.cityUid.setText(); below the if-else block as below.

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Get the data item for this position
        LivingCity city = getItem(position);
        ViewHolder viewHolder; 
    
        if (convertView == null) {
            viewHolder = new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(getContext());
            convertView = inflater.inflate(R.layout.object_main_citydetailsrow, parent, false);
    
            viewHolder.cityUid  = (TextView) convertView.findViewById(R.id.cityrow_uid);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
    
        //city.getUid() returns an int
        viewHolder.cityUid.setText(String.format(Locale.ENGLISH,"#%d", city.getUid()));
    
        // Return the completed view to render on screen
        return convertView;
    }