androidandroid-fragmentsgridviewadapterbaseadapter

First element pops up at the end of list in GridView adapter


So I have DashboardFragment with grid view of items, when last item in grid is pressed it opens new activity for adding new category to database, after adding it to database that activity closes and grid in DashboardFragment should update with new element at the end of grid. Here is the code that I've came up with, the problem with it is that whenever I add new category, grid updates but with first element of list at the end of grid. When I restart application, new element is at the end and everything shows as it has to, so it adds element to database properly. At debugger both lists (one in adapter and one in main class) are the same and with new element at the end. I've spent half a day searching for solution, but i can't find what causes this problem.


public class DashboardFragment extends Fragment {

    private DashboardViewModel dashboardViewModel;
    List<Category> categories = new ArrayList<>();
    PieChart chart;

    CategoryAdapter adapter;

    DBHelper dbHelper;
    SQLiteDatabase database;


    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        dashboardViewModel = ViewModelProviders.of(this).get(DashboardViewModel.class);
        View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
        setHasOptionsMenu(true);

        //-----Database
        dbHelper = new DBHelper(super.getContext());
        //-----Categories
        ParseCategories();
        final ExpandableHeightGridView gridView = (ExpandableHeightGridView) root.findViewById(R.id.category_grid);
        adapter = new CategoryAdapter(root.getContext(), categories);
        gridView.setAdapter(adapter);
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (position == categories.size() - 1) {
                    startActivityForResult(new Intent(DashboardFragment.super.getContext(), CategoryCreation.class),1);

                }
            }
        });
        gridView.setExpanded(true);
        //-----Chart
        chart = (PieChart) root.findViewById(R.id.chart);
        setupPieChart();
        return root;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode==1){
            ParseCategories();
            adapter.updateCategoryList(categories);
        }
    }

    public void ParseCategories() {
        categories.clear();
        database = dbHelper.getWritableDatabase();
        Cursor c = database.rawQuery("select * from categories", null);
        c.moveToFirst();
        for (int i = 0; i < c.getCount(); i++) {
            int id = c.getInt(c.getColumnIndex("_id"));
            String name = c.getString(c.getColumnIndex("name"));
            double amount = c.getDouble(c.getColumnIndex("amount"));
            int imageId = c.getInt(c.getColumnIndex("iconId"));
            categories.add(new Category(id, name, amount, imageId));
            c.moveToNext();
        }
        categories.add(new Category(-1, "Add", -1, R.drawable.add_category_icon));
        c.close();
        database.close();
    }

    public class CategoryAdapter extends BaseAdapter {
        private Context context;
        private List<Category> categories;

        public CategoryAdapter(Context context, List<Category> cats) {
            this.context = context;
            this.categories = cats;
        }

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

            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            View gridView;

            if (convertView == null) {

                gridView = new View(context);

                gridView = inflater.inflate(R.layout.dashboard_category_item, null);

                TextView nameView = (TextView) gridView.findViewById(R.id.category_name);
                ImageView imageView = (ImageView) gridView.findViewById(R.id.grid_item_image);
                TextView amountView = (TextView) gridView.findViewById(R.id.category_amount);

                nameView.setText(categories.get(position).getName());
                imageView.setImageResource(categories.get(position).getIconId());
                if (categories.get(position).getAmount() >= 0) {
                    amountView.setText("$" + categories.get(position).getAmount());
                } else {
                    amountView.setText("");
                }

            } else {
                gridView = (View) convertView;
            }

            return gridView;
        }

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

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        public void updateCategoryList(List<Category> newlist) {
            this.notifyDataSetChanged();
        }
    }
}

Solution

  • Try this:

        public View getView(int position, View convertView, ViewGroup parent) {
    
            View gridView;
            if (convertView == null) {
                gridView = new View(context);
                LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                gridView = inflater.inflate(R.layout.dashboard_category_item, null);
            } else {
                gridView = (View) convertView;
            }
    
            TextView nameView = (TextView) gridView.findViewById(R.id.category_name);
            ImageView imageView = (ImageView) gridView.findViewById(R.id.grid_item_image);
            TextView amountView = (TextView) gridView.findViewById(R.id.category_amount);
    
            nameView.setText(categories.get(position).getName());
            imageView.setImageResource(categories.get(position).getIconId());
            if (categories.get(position).getAmount() >= 0) {
                amountView.setText("$" + categories.get(position).getAmount());
            } else {
                amountView.setText("");
            }
    
            return gridView;
        }
    

    Explanation: For better memory usage and efficiency, adpater is designed to reuse/recycle item views. One situation is scrolling, some items disappear and adapter gets the disappeared views from the argument convertView which can be used for the new items. But these convertViews contains old data, so they need to be populated with new data. Similar situation is on notifyDataSetChanged(). Therefore the codes to populate data in the item view should be after if (convertView == null) {...}else{...}.