Populate Spinner from LiveData (Room Database)
Asked Answered
F

1

6

Where I am

I am attempting to fill a spinner with data from a database, using Room. The data is a list of terms, which have courses associated with them.

I want to use the spinner when creating a new course, to select a term to associate it with.

Currently, the spinner does not show a default option, but if you click the spinner it shows a list of the data to select from. After you select something from the spinner, it does not show what you have selected.

spinner before/after a selection has been made

spinner showing the available options

Here is my code for loading data into the spinner's adapter:

        termsList = new ArrayList<>();
        termIdList = new ArrayList<>();
        mTermViewModel = new ViewModelProvider(this).get(TermViewModel.class);
        mTermViewModel.getAllTerms().observe(this, new Observer<List<TermEntity>>() {
            @Override
            public void onChanged(@Nullable final List<TermEntity> terms) {
                for (TermEntity term : terms) {
                    termsList.add(term.getTermName());
                    termIdList.add(term.getTermId());
                }
            }
        });

        ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, termsList);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mTermSpinner.setAdapter(adapter);
    }

Here is the TermDAO

@Dao
public interface TermDAO {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(TermEntity term);

    @Query("DELETE FROM terms")
    void deleteAllTerms();

    @Query("SELECT * FROM terms ORDER BY termId ASC")
    LiveData<List<TermEntity>> getAllTerms();

    @Query("SELECT * FROM terms WHERE termId = :termId")
    LiveData<List<TermEntity>> getTerm(int termId);

    @Query("SELECT termId FROM terms WHERE termName = :termName")
    LiveData<List<TermEntity>> getTermIdByTermName(String termName);
}

Here is the TermViewModel

public class TermViewModel extends AndroidViewModel {
    private SchoolTrackerRepository mRepository;
    private LiveData<List<TermEntity>> mAllTerms;

    public TermViewModel(@NonNull Application application) {
        super(application);
        mRepository = new SchoolTrackerRepository(application);
        mAllTerms = mRepository.getAllTerms();
    }

    public LiveData<List<TermEntity>> getAllTerms() {
        return mAllTerms;
    }

    public void insert(TermEntity termEntity) {
        mRepository.insert(termEntity);
    }
}

I want to show the term name in the spinner and use the corresponding termId to perform the query.

What I've tried

I have tried using Mutable LiveData instead of LiveData but when I attempted to run it, I got an error that said:

Error:Not sure how to convert a Cursor to this method's return type

I'm really at a loss. Thanks for any help.

Added Repository

public class SchoolTrackerRepository {
    private TermDAO mTermDAO;
    private CourseDAO mCourseDAO;
    int termId;
    private LiveData<List<TermEntity>> mAllTerms;
    private LiveData<List<CourseEntity>> mAllCourses;
    private LiveData<List<CourseEntity>> mAssociatedCourses;

    public SchoolTrackerRepository(Application application) {
        SchoolTrackerDatabase db = SchoolTrackerDatabase.getDatabase(application);
        mTermDAO = db.termDAO();
        mCourseDAO = db.courseDAO();

        mAllTerms = mTermDAO.getAllTerms();
        mAllCourses = mCourseDAO.getAllCourses();
    }

    public LiveData<List<TermEntity>> getAllTerms() {
        return mAllTerms;
    }

    public LiveData<List<CourseEntity>> getAllCourses() {
        return mAllCourses;
    }

    public LiveData<List<CourseEntity>> getmAssociatedCourses(int termId) {
        return mAssociatedCourses;
    }

    public void insert(TermEntity termEntity) {
        new insertAsyncTask1(mTermDAO).execute(termEntity);
    }

    private static class insertAsyncTask1 extends AsyncTask<TermEntity, Void, Void> {
        private TermDAO mAsyncTaskDAO;

        insertAsyncTask1(TermDAO dao) {
            mAsyncTaskDAO = dao;
        }

        @Override
        protected Void doInBackground(final TermEntity... params) {
            mAsyncTaskDAO.insert(params[0]);
            return null;
        }
    }

    public void insert(CourseEntity courseEntity) {
        new insertAsyncTask2(mCourseDAO).execute(courseEntity);
    }

    private static class insertAsyncTask2 extends AsyncTask<CourseEntity, Void, Void> {
        private CourseDAO mAsyncCourseDAO;

        insertAsyncTask2(CourseDAO dao) {
            mAsyncCourseDAO = dao;
        }

        @Override
        protected Void doInBackground(final CourseEntity... params) {
            mAsyncCourseDAO.insert(params[0]);
            return null;
        }
    }
}
Flyman answered 16/12, 2019 at 2:55 Comment(2)
also write down your SchoolTrackerRepository code hereWardship
I have added the repository.Flyman
A
8

I guess that you missing notifyDataSetChanged after update the termsList variable.

Take a try with this:

ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, termsList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mTermSpinner.setAdapter(adapter);

mTermViewModel = new ViewModelProvider(this).get(TermViewModel.class);
mTermViewModel.getAllTerms().observe(this, new Observer<List<TermEntity>>() {
    @Override
    public void onChanged(@Nullable final List<TermEntity> terms) {
        for (TermEntity term : terms) {
            termsList.add(term.getTermName());
            termIdList.add(term.getTermId());
        }

        //notifyDataSetChanged after update termsList variable here
        adapter.notifyDataSetChanged();
    }
});
Arc answered 16/12, 2019 at 3:9 Comment(2)
That did it! I had to declare the adapter before onChanged(). Thank you so much.Flyman
I have tried above code, but my observer is called twice. So in spinner, items are displayed twice. How to solve it.Ligneous

© 2022 - 2024 — McMap. All rights reserved.