Search code examples
sqlitelistviewindexoutofboundsexceptionbaseadapterlistadapter

ListView/adapter throwing IndexOutOfBound


I have a listview with a getCount() of 7. I want all 7 items to be shown regardless if any data from my database is available to populate them. If no data is available then an item should just be blank with predetermined text.

When I have not hardcoded 7 database entries beforehand to go into the 7 views then I get an indexoutofbound exception when running the app due to the 7 items not being able to be populated accordingly. This happens in ListMealsAdapter.java when method Meal currentItem = getItem(position); is called and triggers public Meal getItem(int position).

I am looking for a condition statement that I can use for my listview/adapter that can handle an empty database so that the index does not go out of bounds. Also, is the BaseAdapter suited for what I want to do?

MainActivity.java

public class MainActivity extends BaseActivity {
public static final String TAG = "MainActivity";

private ListView mListviewMeals;
private MealDAO mMealDao;
private List<Meal> mListMeals;
private ListMealsAdapter mAdapter;
private SQLiteDatabase mDatabase;
DatabaseHelper mDbHelper;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    activateToolbar(1);

    // initialize views
    initViews();

    // fill the dailyListView
    mMealDao = new MealDAO(this);
    mListMeals = mMealDao.getAllMeals();
    mAdapter = new ListMealsAdapter(this, mListMeals, MainActivity.this);
    mListviewMeals.setAdapter(mAdapter);
}

private void initViews() {
    this.mListviewMeals = (ListView) findViewById(R.id.view_daily_list);
}

ListMealsAdapter.java

public class ListMealsAdapter extends BaseAdapter {
    public static final String TAG = "ListMealsAdapter";

    Activity mActivity;
    private List<Meal> mItems;
    private LayoutInflater mInflater;

    public ListMealsAdapter(Context context, List<Meal> listMeals, Activity activity) {
        super();
        mActivity = activity;
        this.setItems(listMeals);
        this.mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return 7;
    }

    @Override
    public Meal getItem(int position) {
        return (getItems() != null && !getItems().isEmpty()) ? getItems().get(position) : null;
    }

    @Override
    public long getItemId(int position) {
        return (getItems() != null && !getItems().isEmpty()) ? getItems().get(position).getId() : position;
    }

    @Override
    public View getView(int position, final View convertView, final ViewGroup parent) {
        View v = convertView;
        final ViewHolder holder;
        if (v == null) {
            v = mInflater.inflate(R.layout.list_item_daily, parent, false);
            holder = new ViewHolder();
            holder.txtDescription = (TextView) v.findViewById(R.id.txtBreakfast);
            v.setTag(holder);
        } else {
            holder = (ViewHolder) v.getTag();
        }

        // fill row data
        Meal currentItem = getItem(position);
        if (currentItem != null) {
            holder.txtDescription.setText(currentItem.getDescription());
        }

        return v;
    }

    public List<Meal> getItems() {
        return mItems;
    }

    public void setItems(List<Meal> mItems) {
        this.mItems = mItems;
    }

    class ViewHolder {
        TextView txtDescription;
    }
}

Meal.java

public class Meal implements Serializable {
    public static final String TAG = "Meal";
    private static final long serialVersionUID = -7406082437623008161L;

    private long mId;
    private int mType;
    private String mDescription;

    public Meal() {
    }

    public Meal(int type, String description) {
        this.mType = type;
        this.mDescription = description;
    }

    public long getId() {
        return mId;
    }

    public void setId(long mId) {
        this.mId = mId;
    }

    public int getType() {
        return mType;
    }

    public void setType(int mType) {
        this.mType = mType;
    }

    public String getDescription() {
        return mDescription;
    }

    public void setDescription(String mDescription) {
        this.mDescription = mDescription;
    }
}

MealDAO.java

public class MealDAO {
    public static final String TAG = "MealDAO";

    private SQLiteDatabase mDatabase;
    private DatabaseHelper mDbHelper;
    private Context mContext;
    private String[] mAllColumns = { DatabaseHelper.COLUMN_MEAL_ID,
            DatabaseHelper.COLUMN_MEAL_TYPE, DatabaseHelper.COLUMN_MEAL_DESCRIPTION};

    public MealDAO(Context context) {
        this.mContext = context;
        mDbHelper = new DatabaseHelper(context);
        // open the database
        try {
            open();
        } catch (SQLException e) {
            Log.e(TAG, "SQLException on opening database " + e.getMessage());
            e.printStackTrace();
        }
    }

    public void open() throws SQLException {
        mDatabase = mDbHelper.getWritableDatabase();
    }

    public void close() {
        mDbHelper.close();
    }

    public List<Meal> getAllMeals() {
        List<Meal> listMeals = new ArrayList<Meal>();

        Cursor query = mDatabase.rawQuery("SELECT * from meal", null);
        if(query.moveToFirst()) {
            do {

                // Cycle through all records
                Meal meal = cursorToMeal(query);
                listMeals.add(meal);
            } while(query.moveToNext());
        }

        return listMeals;
    }

    public Meal getMealById(long id) {
        Cursor cursor = mDatabase.query(DatabaseHelper.TABLE_MEALS, mAllColumns,
                DatabaseHelper.COLUMN_MEAL_ID + " = ?",
                new String[] { String.valueOf(id) }, null, null, null);
        if (cursor != null) {
            cursor.moveToFirst();
        }

        Meal meal = cursorToMeal(cursor);
        return meal;
    }

    protected Meal cursorToMeal(Cursor cursor) {
        Meal meal = new Meal();
        meal.setId(cursor.getLong(0));
        meal.setType(cursor.getInt(1));
        meal.setDescription(cursor.getString(2));
        return meal;
    }
}

Solution

  • After a LOT of trial and error I finally found an acceptable solution to my problem. What I did was to add a default row to my database for the view items that I wanted to have a predetermined database entry when no data had been entered beforehand.

    I then made sure to start at index 2, making sure that index 1 would be reserved for my default value. If the index comes out of bounds then the exception is caught and the default database entry will be added to the array.

    public Meal getItem(int position) {
        Meal result;
        try {
            result = (getItems() != null && !getItems().isEmpty()) ? getItems().get(position) : null;
        } catch (Exception e) {
            Meal default = getItem(0);
            return default;
        }
        return result;
    }
    
    Meal currentItem = getItem(position + 1);
            if (currentItem != null) {
                holder.txtDescription.setText(currentItem.getDescription());
            }
    

    With that change things have been running smooth ever since. I hope this can help someone else as well.