Search code examples
androidlistviewcustom-lists

Android list items are changing when scrolling


I have a list that should have each item the same layout except for the first and last item which should have separate layouts. The first item shows its unique layout correctly, but the last one changes when I scroll it in and out of view between the correct one and the layout of the first item. Here is my adapter:

public class ListAdapterApp extends ArrayAdapter<App> {

    Context context;
    int layoutResourceId;
    List<App> appList;

    public ListAdapterApp(Context context, int layoutResourceId, List<App> appList) {
        super(context, layoutResourceId, appList);
        this.layoutResourceId = layoutResourceId;
        this.context = context;
        this.appList = appList;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;
        AppHolder holder = null;

        if(row == null)
        {


            holder = new AppHolder();
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            if(position == 0){
                holder.rowLayoutID = R.layout.list_column_appheaderitem;
            } else if(position == appList.size() - 1){
                holder.rowLayoutID = R.layout.list_column_appfooteritem;
            } else {
                holder.rowLayoutID = layoutResourceId;
            }
            row = inflater.inflate(holder.rowLayoutID, parent, false);//holder.packBackground = (LinearLayout)row.findViewById(R.id.packBackground);

            row.setTag(holder);
        }
        else
        {
            holder = (AppHolder)row.getTag();
        }

        App app = appList.get(position);

        return row;
    }

    static class AppHolder
    {
        public int rowLayoutID;
    }
}

Solution

  • The problem is ListView is recycling a non-footer row view when it calls getView() for the last item. You need to implement getItemViewType() and getViewTypeCount() in your adapter, to give ListView a hint about what views can be recycled for those items.

    private static final int VIEW_TYPE_HEADER = 0;
    private static final int VIEW_TYPE_FOOTER = 1;
    private static final int VIEW_TYPE_DEFAULT = 2;
    private static final int VIEW_TYPE_COUNT = 3;
    
    @Override
    public int getViewTypeCount() {
        // The total number of row types your adapter supports.
        // This should NEVER change at runtime.
        return VIEW_TYPE_COUNT;
    }
    
    @Override
    public int getItemViewType(int position) {
        // return a value from zero to (viewTypeCount - 1)
        if (position == 0) {
            return VIEW_TYPE_HEADER;
        } else if (position == getCount() - 1) {
            return VIEW_TYPE_FOOTER;
        }
        return VIEW_TYPE_DEFAULT;
    }
    

    Inside of getView() you can call getItemViewType() and use a switch statement to build your rows:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        switch(getItemViewType(position)) {
        case VIEW_TYPE_HEADER:
            return getHeaderView(position, convertView, parent); // TODO implement this
        case VIEW_TYPE_FOOTER:
            return getFooterView(position, convertView, parent); // TODO implement this
        case VIEW_TYPE_DEFAULT:
            return getDefaultView(position, convertView, parent); // TODO implement this
        }
    }