LiveData observer onChanged is not called - android

I'm trying to use Room database in my Quiz application.
I couldn't get list of questions from database. here is the code I'm using :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.new_exam, container, false);
queViewModel=ViewModelProviders.of(this).get(QueViewModel.class);
queViewModel.getQuizList(examType).observe(this, new Observer<List<QueEnt>>() {
#Override
public void onChanged(List<QueEnt> queEnts) {
Log.e(TAG, "onChanged is called!"); // this line never shows in the Logcat
questionsList2 = queEnts;
}
});
and this is the ViewModel :
public class QueViewModel extends AndroidViewModel {
private QueRepository repository;
private LiveData<List<QueEnt>> quesList;
public QueViewModel(#NonNull Application application) {
super(application);
repository=new QueRepository(application);
quesList=repository.getQues();
}
public void insert(QueEnt queEnt){
repository.insert(queEnt);
}
public void delete(QueEnt queEnt){
repository.delete(queEnt);
}
public LiveData<List<QueEnt>> getQuizList(int examtype){
Log.e(TAG,"returning a list"); // this appears in the Logcat
return quesList;
}
}
The error I get is
Invalid index 0, size is 0
on questionsList2 , of course because the list was never populated. The onChanged method is not called, as it seems from the Logcat.
How can I fix this ?
Edit :
QueRepository file:
public class QueRepository {
private QueDao queDao;
private LiveData<List<QueEnt>> ques;
public QueRepository(Application application) {
QueDatabase database = QueDatabase.getInstance(application);
queDao = database.queDao();
ques = queDao.getQuesAll();
}
public void insert(QueEnt queEnt) {
new InsertQAsync(queDao).execute(queEnt);
}
public void update(QueEnt queEnt) {
new UpdateQAsync(queDao).execute(queEnt);
}
public void delete(QueEnt queEnt) {
new DeleteQAsync(queDao).execute(queEnt);
}
public LiveData<List<QueEnt>> getQues() {
return ques;
}
private static class InsertQAsync extends AsyncTask<QueEnt, Void, Void> {
private QueDao queDao;
private InsertQAsync(QueDao queDao) {
this.queDao = queDao;
}
#Override
protected Void doInBackground(QueEnt... queEnts) {
queDao.insert(queEnts[0]);
return null;
}
}
private static class UpdateQAsync extends AsyncTask<QueEnt, Void, Void> {
private QueDao queDao;
private UpdateQAsync(QueDao queDao) {
this.queDao = queDao;
}
#Override
protected Void doInBackground(QueEnt... queEnts) {
queDao.update(queEnts[0]);
return null;
}
}
private static class DeleteQAsync extends AsyncTask<QueEnt, Void, Void> {
private QueDao queDao;
private DeleteQAsync(QueDao queDao) {
this.queDao = queDao;
}
#Override
protected Void doInBackground(QueEnt... queEnts) {
queDao.delete(queEnts[0]);
return null;
}
}
}
And QueDao :
#Dao
public interface QueDao {
#Insert
void insert(QueEnt queEnt);
#Update
void update(QueEnt queEnt);
#Delete
void delete(QueEnt queEnt);
#Query("SELECT * FROM questions LIMIT 30" )
LiveData<List<QueEnt>> getQuesAll();
}
And Database :
#Database(entities = {QueEnt.class}, version = 1)
public abstract class QueDatabase extends RoomDatabase {
public static QueDatabase instance;
public static synchronized QueDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(), QueDatabase.class, "que_database")
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
return instance;
}
public abstract QueDao queDao();
private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
new PopulateDbAsync(instance).execute();
}
};
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
private QueDao queDao;
private PopulateDbAsync(QueDatabase db) {
queDao = db.queDao();
}
#Override
protected Void doInBackground(Void... voids) {
queDao.insert(new QueEnt(91, 93, "Question 1?", " Answer 1", "Answer2 ", "Answer 3 ", " Answer4 ", 2, 2, 1,0,0,0,0,0,"","","","",""));
queDao.insert(new QueEnt(93, 95, "Question 2?", " Answer 1", "Answer2 ", "Answer 3 ", " Answer4 ", 3, 2, 4,0,0,0,0,0,"","","","",""));
return null;
}
}
}

Related

Room only prints the first line

I am writing a simple application that stores 2 string values in a database using Room (I am trying to learn this library). So, I have only one line on my list. Presented for the first time. The rest are not displayed. What is the reason for this behavior?
Model
#Entity
public class Note {
#PrimaryKey(autoGenerate = true)
private long id;
private String title;
private String text;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
NoteDao
#Dao
public interface NoteDao {
#Insert
void insert(Note note);
#Delete
void delete(Note note);
#Query("SELECT * FROM Note")
List<Note> getAllNotes();
}
AppDatabase
#Database(entities = Note.class, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract NoteDao getNoteDao();
}
DataManager
public class DataManager {
private AppDatabase appDatabase;
public DataManager (AppDatabase appDatabase) {
this.appDatabase = appDatabase;
}
public List <Note> getNotes () {
try {
return new GetNotes (). execute (). get ();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace ();
return null;
}
}
public void insertNote (Note note) {
new InsertNote (note) .execute ();
}
public void deleteNote (Note note) {
new DeleteNote (note) .execute ();
}
// Get all notes
public class GetNotes extends AsyncTask <Void, Void, List <Note>> {
#Override
protected List <Note> doInBackground (Void ... voids) {
return appDatabase.getNoteDao (). getAllNotes ();
}
}
// Insert note
public class InsertNote extends AsyncTask <Void, Void, Void> {
private Note note;
InsertNote (Note note) {
this.note = note;
}
#Override
protected Void doInBackground (Void ... voids) {
appDatabase.getNoteDao (). insert (note);
return null;
}
}
// Delete note
public class DeleteNote extends AsyncTask <Void, Void, Void> {
private Note note;
public DeleteNote (Note note) {
this.note = note;
}
#Override
protected Void doInBackground (Void ... voids) {
appDatabase.getNoteDao (). delete (note);
return null;
}
}
}
Mvp
public interface NoteListView extends MvpView {
void showNoteList(List<Note> note);
}
Presenter
public class NoteListPresenter extends MvpPresenter<NoteListView> {
private DataManager dataManager;
public NoteListPresenter(DataManager dataManager) {
this.dataManager = dataManager;
}
public void getNotes(){
getView().showNoteList(dataManager.getNotes());
}
public void deleteNote(Note note) {
dataManager.deleteNote(note);
getView().showNoteList(dataManager.getNotes());
}
}
Adapter
public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolder> {
private List<Note> listNote;
private Context context;
public NoteAdapter(Context context, List<Note> listNote) {
this.listNote = listNote;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.title.setText(listNote.get(position).getTitle());
holder.text.setText(listNote.get(position).getText());
}
#Override
public int getItemCount() {
return listNote.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView title, text;
public ViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title);
text = itemView.findViewById(R.id.text);
}
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements NoteListView {
private NoteListPresenter presenter;
private RecyclerView recyclerView;
private NoteAdapter noteAdapter;
private ConstraintLayout constraintLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
constraintLayout = findViewById(R.id.coordinatorMain);
recyclerView = findViewById(R.id.recycler);
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
presenter = new NoteListPresenter(App.getDataManager());
presenter.attachView(this);
presenter.getNotes();
}
public void onNextActivity(View view){
startActivity(new Intent(MainActivity.this, AddNoteActivity.class));
}
#Override
public void showNoteList(List<Note> note) {
noteAdapter = new NoteAdapter(this, note);
recyclerView.setAdapter(noteAdapter);
}
#Override
protected void onResume() {
super.onResume();
presenter.getNotes();
}
#Override
public void showMessage(String message) {
Snackbar.make(constraintLayout, message, Snackbar.LENGTH_SHORT).show();
}
}
UPD
SaveNoteView
public interface SaveNoteView extends MvpView {
void insertNote(Note note);
}
SaveNotePresenter
public class SaveNotePresenter extends MvpPresenter<SaveNoteView> {
private DataManager dataManager;
public SaveNotePresenter(DataManager dataManager) {
this.dataManager = dataManager;
}
public void insertNote(Note note){
dataManager.insertNote(note);
getView().insertNote(note);
}
}
AddNoteActivity
public class AddNoteActivity extends AppCompatActivity implements SaveNoteView {
private TextInputEditText title, text;
private ConstraintLayout constraintLayout;
private SaveNotePresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_note);
constraintLayout = findViewById(R.id.constrainAdd);
title = findViewById(R.id.titleEditText);
text = findViewById(R.id.textEditText);
presenter = new SaveNotePresenter(App.getDataManager());
presenter.attachView(this);
}
//Save a note
public void saveNote(View view){
Note note = new Note();
note.setTitle(title.getText().toString());
note.setText(text.getText().toString());
presenter.insertNote(note);
finish();
}
#Override
public void insertNote(Note note) {
DataManager dataManager = App.getDataManager();
dataManager.insertNote(note);
}
#Override
public void showMessage(String message) {
Snackbar.make(constraintLayout, message, Snackbar.LENGTH_SHORT).show();
}
}
It's pretty likely that there is only 1 id which is beeing replaced the whole time (if this is not the case, check how often insertion is done)
I used Kotlin with Room but in this official example they have a public autogenerated primaryKey, which might be required for Room to access it and autogenerate it. So make the variable public and see if it works
There was no problem. I just specified the parent layout for item_list in full screen. Therefore, the subsequent ones were not visible. Silly mistake:D

Android Room - Get the id of new inserted row form #Insert

In my code below I'm getting rowId. I've read that it's also possible to get the last inserted row id from #Insert directly. In my code I changed void insert to long and tried many other things as in examples I found on the internet, but every time I get errors. Would you like to provide me a code/solution to get the row/user ID from #Insert?
#Dao
public interface UserDao {
#Insert
void insert(UserEntity userEntity);
#Update
void update(UserEntity userEntity);
#Delete
void delete(UserEntity userEntity);
#Query("DELETE FROM user_table")
void deleteAllUsers();
#Query("SELECT * FROM user_table")
LiveData<List<UserEntity>> getAllUsers();
// ====== from here ======
#Query("SELECT * FROM user_table")
LiveData<UserEntity> getRowId();
// ====== till here ======
}
Entity
#Entity(tableName = "user_table")
public class UserEntity {
#PrimaryKey(autoGenerate = true)
private int id;
private String userName;
private String userTelephoneNumber;
public UserEntity(String userName, String userTelephoneNumber) {
this.userName = userName;
this.userTelephoneNumber = userTelephoneNumber;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getUserName() {
return userName;
}
public String getUserTelephoneNumber() {
return userTelephoneNumber;
}
}
Repository
public class UserRepository {
private UserDao userDao;
private LiveData<List<UserEntity>> allUsers;
public UserRepository(Application application) {
HandymanDatabase handymanDatabase = HandymanDatabase.getInstance(application);
userDao = handymanDatabase.userDao();
allUsers = userDao.getAllUsers();
}
public void insert(UserEntity userEntity) {
new InsertUserAsyncTask(userDao).execute(userEntity);
}
public void update(UserEntity userEntity) {
new UpdateUserAsyncTask(userDao).execute(userEntity);
}
public void delete(UserEntity userEntity) {
new DeleteUserAsyncTask(userDao).execute(userEntity);
}
public void deleteAllUsers() {
new DeleteAllUsersAsyncTask(userDao).execute();
}
public LiveData<List<UserEntity>> getAllUsers() {
return allUsers;
}
// ====== from here ======
public LiveData<UserEntity> getRowId() {
return userDao.getRowId();
}
// ====== till here ======
private static class InsertUserAsyncTask extends AsyncTask<UserEntity, Void, Void> {
private UserDao userDao;
private InsertUserAsyncTask(UserDao userDao) {
this.userDao = userDao;
}
#Override
protected Void doInBackground(UserEntity... userEntities) {
userDao.insert(userEntities[0]);
return null;
}
}
private static class UpdateUserAsyncTask extends AsyncTask<UserEntity, Void, Void> {
private UserDao userDao;
private UpdateUserAsyncTask(UserDao userDao) {
this.userDao = userDao;
}
#Override
protected Void doInBackground(UserEntity... userEntities) {
userDao.update(userEntities[0]);
return null;
}
}
private static class DeleteUserAsyncTask extends AsyncTask<UserEntity, Void, Void> {
private UserDao userDao;
private DeleteUserAsyncTask(UserDao userDao) {
this.userDao = userDao;
}
#Override
protected Void doInBackground(UserEntity... userEntities) {
userDao.delete(userEntities[0]);
return null;
}
}
private static class DeleteAllUsersAsyncTask extends AsyncTask<Void, Void, Void> {
private UserDao userDao;
private DeleteAllUsersAsyncTask(UserDao userDao) {
this.userDao = userDao;
}
#Override
protected Void doInBackground(Void... voids) {
userDao.deleteAllUsers();
return null;
}
}
}
ViewModel
public UserViewModel(#NonNull Application application) {
super(application);
userRepository = new UserRepository(application);
allUsers = userRepository.getAllUsers();
}
public void insert(UserEntity userEntity) {
userRepository.insert(userEntity);
}
public void update(UserEntity userEntity) {
userRepository.update(userEntity);
}
public void delete(UserEntity userEntity) {
userRepository.delete(userEntity);
}
public void deleteAllUsers() {
userRepository.deleteAllUsers();
}
public LiveData<List<UserEntity>> getAllUsers() {
return allUsers;
}
// ====== from here ======
public LiveData<UserEntity> getRowId() {
return userRepository.getRowId();
}
// ====== till here ======
}
Fragment/Activity
public class UserFavoritesFragment extends Fragment {
private static final String TAG = "UserFavoritesFragment";
private UserViewModel userViewModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = getLayoutInflater().inflate(R.layout.fragment_user_favorites, container, false);
final TextView textViewUserName = view.findViewById(R.id.textViewUserName);
TextView textViewUserPhone = view.findViewById(R.id.textViewUserPhone);
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
// ====== from here ======
userViewModel.getRowId().observe(getViewLifecycleOwner(), new Observer<UserEntity>() {
#Override
public void onChanged(UserEntity userEntity) {
long rowId = userEntity.getId();
Log.d(TAG, "onChanged: " + rowId);
}
});
// ====== till here ======
return view;
}
}
You can do that using a listener interface that has a callback that accepts a long value of the inserted row id in the database.
Listener Interface
public interface NewIdListener {
void onInsert(long id);
}
Dao
#Dao
public interface UserDao {
#Insert
long insert(UserEntity userEntity); // return id of the inserted userEntity
}
Repository
public class UserRepository {
private Executor mExecutor = Executors.newSingleThreadExecutor();
private UserDao userDao;
...
public void insertUserEntity(final UserEntity entity, final NewIdListener listener) {
mExecutor.execute(new Runnable() {
#Override
public void run() {
listener.onInsert(userDao.insert(entity));
}
});
}
ViewModel
public void insertUserEntity(UserEntity entity, NewIdListener listener) {
userRepository.insertUserEntity(entity, listener);
}
Activity
userViewModel.insertUserEntity(new UserEntity("User Name", "12345678"), new NewIdListener() {
#Override
public void onInsert(final long id) {
requireActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(requireActivity(), "Id: " + id, Toast.LENGTH_SHORT).show();
}
});
}
});
Note: For background thread, I've used Executor instead of AsyncTask as AsyncTask is deprecated now.

Problem with Live Data observers in Room DB Android

I have problem with observing 3 different lists with live data observer for viewing them in one single recycler view.
This is the scenario: In ListsFragment, I have TabLayout with 3 tabs (WANT, PLAYING, PLAYED) and 1 recycler view. On different tab click I load different lists of games from database and I am presenting them with Live Data observer pattern in the recycler view. Until this moment everything works OK.
The problem occurs when I press one game DELETE button, the database is updated but in the recycler view is loading wrong list and not the list of the selected tab (Probably because there are 3 active observers), so the data on the screen is updated with wrong games.
Gif of the behaviour:
https://gfycat.com/showyembarrassedbarasingha
Can anyone please help me, I tried many solutions but nothing helped and I am stuck more than 1 month.
This is the code.
Fragment:
public class ListsFragment extends Fragment implements VisitedGameInterface {
public static final String TAG = ListsFragment.class.getSimpleName();
private static final int WANT = 0;
private static final int PLAYING = 1;
private static final int PLAYED = 2;
private int selectedTab = WANT;
private GameViewModel gameViewModel;
private GameVisitedAdapter gameAdapter;
private TextView noGamesTV;
public ListsFragment() {
// Required empty public constructor
}
public static ListsFragment newInstance(Bundle bundle) {
ListsFragment fragment = new ListsFragment();
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_lists, container, false);
TabLayout listsTabLayout = view.findViewById(R.id.lists_tab_layout);
RecyclerView listsRecyclerView = view.findViewById(R.id.lists_recycler_view);
noGamesTV = view.findViewById(R.id.no_games_tv);
listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.want)), true);
listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.playing)));
listsTabLayout.addTab(listsTabLayout.newTab().setText(getString(R.string.played)));
listsTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
listsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), RecyclerView.VERTICAL, false));
listsRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
gameAdapter = new GameVisitedAdapter(getActivity(), this);
listsRecyclerView.setAdapter(gameAdapter);
gameViewModel = ViewModelProviders.of(this).get(GameViewModel.class);
gameViewModel.getWantedGames().observe(this, new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> wantedGames) {
if (wantedGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
}else {
noGamesTV.setText(R.string.no_wanted_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(wantedGames);
gameAdapter.notifyDataSetChanged();
}
});
listsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
if (getActivity() != null && tab.getText() != null) {
if (tab.getText().equals("WANT")) {
selectedTab = tab.getPosition();
gameViewModel.getWantedGames().observe(getActivity(), new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> wantedGames) {
if (wantedGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
} else {
noGamesTV.setText(R.string.no_wanted_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(wantedGames);
gameAdapter.notifyDataSetChanged();
}
});
} else if (tab.getText().equals("PLAYING")) {
selectedTab = tab.getPosition();
gameViewModel.getPlayingGames().observe(getActivity(), new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> playingGames) {
if (playingGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
} else {
noGamesTV.setText(R.string.no_playing_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(playingGames);
gameAdapter.notifyDataSetChanged();
}
});
} else if (tab.getText().equals("PLAYED")){
selectedTab = tab.getPosition();
gameViewModel.getPlayedGames().observe(getActivity(), new Observer<List<Game>>() {
#Override
public void onChanged(List<Game> playedGames) {
if (playedGames.size() != 0) {
noGamesTV.setVisibility(View.GONE);
} else {
noGamesTV.setText(R.string.no_played_games);
noGamesTV.setVisibility(View.VISIBLE);
}
gameAdapter.setGames(playedGames);
gameAdapter.notifyDataSetChanged();
}
});
}
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
return view;
}
#Override
public void onGameDelete(final Game game) {
if (selectedTab == WANT){
game.setWanted(false);
showSnackBar(game, WANT);
} if (selectedTab == PLAYING){
game.setPlaying(false);
showSnackBar(game, PLAYING);
} if (selectedTab == PLAYED){
game.setPlayed(false);
showSnackBar(game, PLAYED);
}
gameViewModel.update(game);
gameAdapter.notifyDataSetChanged();
}
#Override
public void onVisitedGameClick(Game game) {
Intent intent = new Intent(getActivity(), GameActivity.class);
intent.putExtra("game", game);
startActivity(intent);
}
private void showSnackBar(final Game snackGame, final int selectedTab){
Snackbar snackbar = Snackbar.make(Objects.requireNonNull(getView()), snackGame.getName() + " deleted", Snackbar.LENGTH_LONG)
.setAction(R.string.undo, new View.OnClickListener() {
#Override
public void onClick(View view) {
if (selectedTab == WANT) snackGame.setWanted(true);
if (selectedTab == PLAYING) snackGame.setPlaying(true);
if (selectedTab == PLAYED) snackGame.setPlayed(true);
gameViewModel.insert(snackGame);
}
});
snackbar.show();
}
}
XML of ListsFragment
<RelativeLayout
android:id="#+id/lists_layout" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context=".fragment.ListsFragment">
<com.google.android.material.tabs.TabLayout
android:id="#+id/lists_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabSelectedTextColor="#color/button_blue"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/lists_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/lists_tab_layout"/>
<TextView
android:id="#+id/no_games_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:text="No games at this moment"
android:textColor="#color/text_black"
android:visibility="invisible"/>
</RelativeLayout>
The adapter:
public class GameVisitedAdapter extends RecyclerView.Adapter<GameVisitedAdapter.GameVisitedViewHolder> {
private Context context;
private LayoutInflater inflater;
private List<Game> games = new ArrayList<>();
private VisitedGameInterface visitedGameInterface;
public GameVisitedAdapter(Context context, VisitedGameInterface visitedGameInterface){
this.context = context;
inflater = LayoutInflater.from(context);
this.visitedGameInterface = visitedGameInterface;
}
public void setGames(List<Game> games){
this.games = games;
}
#NonNull
#Override
public GameVisitedViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.item_game_visited, parent, false);
return new GameVisitedViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull GameVisitedViewHolder holder, int position) {
Game game = games.get(position);
if (game.getCover() != null) {
Glide
.with(context)
.load(context.getString(R.string.cover_url) + game.getCover().getImage_id() + ".jpg")
.centerCrop()
.placeholder(context.getResources().getDrawable(R.drawable.placeholder))
.into(holder.gameCover);
}
holder.gameTitle.setText(game.getName());
SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy");
String dateString = formatter.format((new Date((long) game.getFirst_release_date() * 1000)));
holder.gameYear.setText(dateString);
}
#Override
public int getItemCount() {
return games.size();
}
class GameVisitedViewHolder extends RecyclerView.ViewHolder {
private ImageView gameCover, gameDeleteButton;
private TextView gameTitle, gameYear;
GameVisitedViewHolder(#NonNull View itemView) {
super(itemView);
gameCover = itemView.findViewById(R.id.game_cover);
gameTitle = itemView.findViewById(R.id.game_name);
gameYear = itemView.findViewById(R.id.game_release_year);
gameDeleteButton = itemView.findViewById(R.id.game_delete_button);
gameDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
visitedGameInterface.onGameDelete(games.get(getAdapterPosition()));
}
});
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
visitedGameInterface.onVisitedGameClick(games.get(getAdapterPosition()));
}
});
}
}
}
The ViewModel
public class GameViewModel extends AndroidViewModel {
private GameRepository gameRepository;
public GameViewModel(#NonNull Application application) {
super(application);
gameRepository = new GameRepository(application);
}
public void insert(Game game){
gameRepository.insert(game);
}
public void update(Game game){
gameRepository.update(game);
}
public void deleteGame(Game game){
gameRepository.delete(game);
}
public void deleteAll(){
gameRepository.deleteAll();
}
public void DeleteVisitedGames(){ gameRepository.deleteVisitedGames();}
public LiveData<List<Game>> getWantedGames(){
return gameRepository.getWantedGames();
}
public LiveData<List<Game>> getPlayingGames(){
return gameRepository.getPlayingGames();
}
public LiveData<List<Game>> getPlayedGames(){
return gameRepository.getPlayedGames();
}
public LiveData<List<Game>> getVisitedGames(){
return gameRepository.getVisitedGames();
}
public LiveData<List<Game>> getAllGamesFromDB(){
return gameRepository.getAllGamesFromDB();
}
}
The Repository:
public class GameRepository {
private GameDao gameDao;
private LiveData<List<Game>> wantedGames;
private LiveData<List<Game>> playingGames;
private LiveData<List<Game>> playedGames;
private LiveData<List<Game>> visitedGames;
private LiveData<List<Game>> allGamesFromDB;
public GameRepository(Application application){
GameDatabase gameDatabase = GameDatabase.getInstance(application);
gameDao = gameDatabase.gameDao();
wantedGames = gameDao.getWantedGames();
playingGames = gameDao.getPlayingGames();
playedGames = gameDao.getPlayedGames();
visitedGames = gameDao.getVisitedGames();
allGamesFromDB = gameDao.getAllGamesFromDB();
}
public LiveData<List<Game>> getWantedGames(){
return wantedGames;
}
public LiveData<List<Game>> getPlayingGames(){
return playingGames;
}
public LiveData<List<Game>> getPlayedGames(){
return playedGames;
}
public LiveData<List<Game>> getVisitedGames(){
return visitedGames;
}
public LiveData<List<Game>> getAllGamesFromDB(){
return allGamesFromDB;
}
public void insert(Game game){
new InsertAsyncTask(gameDao).execute(game);
}
public void update(Game game){
new UpdateAsyncTask(gameDao).execute(game);
}
public void delete(Game game){
new DeleteItemAsyncTask(gameDao).execute(game);
}
public void deleteAll(){
new DeleteAllAsyncTask(gameDao).execute();
}
public void deleteVisitedGames() {new DeleteVisitedAsyncTask(gameDao).execute();}
//-------------INSERT GAME ASYNC TASK
private static class InsertAsyncTask extends AsyncTask<Game, Void, Void> {
private GameDao asyncDao;
public InsertAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Game... games) {
asyncDao.insert(games[0]);
return null;
}
}
//-------------UPDATE GAME ASYNC TASK
private static class UpdateAsyncTask extends AsyncTask<Game, Void, Void> {
private GameDao asyncDao;
public UpdateAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Game... games) {
asyncDao.updateGame(games[0]);
return null;
}
}
//----------DELETE GAME ASYNC TASK
private static class DeleteItemAsyncTask extends AsyncTask <Game, Void, Void> {
private GameDao asyncDao;
public DeleteItemAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Game... games) {
asyncDao.deleteGame(games[0]);
return null;
}
}
//-------------DELETE ALL ASYNC TASK
private static class DeleteAllAsyncTask extends AsyncTask <Void, Void, Void>{
private GameDao asyncDao;
public DeleteAllAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Void... voids) {
asyncDao.deleteAllGames();
return null;
}
}
//-------------DELETE VISITED GAMES ASYNC TASK
private static class DeleteVisitedAsyncTask extends AsyncTask <Void, Void, Void>{
private GameDao asyncDao;
public DeleteVisitedAsyncTask(GameDao asyncDao){
this.asyncDao = asyncDao;
}
#Override
protected Void doInBackground(Void... voids) {
asyncDao.deleteVisitedGames();
return null;
}
}
}
The DAO:
#Dao
public interface GameDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Game game);
#Delete
void deleteGame(Game game);
#Update
void updateGame(Game game);
#Query("DELETE FROM game_table")
void deleteAllGames();
#Query("SELECT * FROM game_table WHERE isWanted")
LiveData<List<Game>> getWantedGames();
#Query("SELECT * FROM game_table WHERE isPlaying")
LiveData<List<Game>> getPlayingGames();
#Query("SELECT * FROM game_table WHERE isPlayed")
LiveData<List<Game>> getPlayedGames();
#Query("SELECT * FROM game_table WHERE isVisited")
LiveData<List<Game>> getVisitedGames();
#Query("SELECT * FROM game_table")
LiveData<List<Game>> getAllGamesFromDB();
#Query("DELETE FROM game_table WHERE isVisited")
void deleteVisitedGames();
}
Database:
#Database(entities = Game.class, version = 8, exportSchema = false)
#TypeConverters({Converters.class})
public abstract class GameDatabase extends RoomDatabase {
public abstract GameDao gameDao();
private static GameDatabase INSTANCE;
public static GameDatabase getInstance(Context context){
if (INSTANCE == null){
synchronized (GameDatabase.class){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
GameDatabase.class, "game_database")
.fallbackToDestructiveMigration()
.addCallback(sRoomDatabaseCallback)
.build();
}
}
}
return INSTANCE;
}
private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback(){
#Override
public void onOpen(#NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
}
};
}
My 2cents: even after you change tabs, your observers are still active, as you are using the same table for everything and I believe (just glance at the code) you are updating your game entry (e.g: isPlayed set to false) thus it send updates to all active observers thus sending update to the recylerview.
With that in mind, in case I am right here, you can:
Make 3 distinct tables (can be in the same database)
use viewpager to have 3 fragments with 3 diffent recyclers (actually you can use viewpager2 with a viewholder instead of making fragments)
have 3 recyclers (and you do a visibility.Gone every time user changes the tab)
In case I am not right, option 2 e 3 still solve your problem. I hope it helps!
p.s: you could also remove and insert observers everytime users changes tab, I find it to be less straightforward though.

LiveData not calling Observer on Adding new entry to Room DB

I am using Room DB to store user set Price Alerts.
The Alerts list is shown in a Fragment with a button which launches new activity to add new entry.
When I add a new entry and go back to fragment, the RecyclerView isn't being updated even though I am returning LiveData and added an Observer.
I have tried multiple answers here like Using a MutableLiveData instead of LiveData and few other but it still isn't working.
I have tried multiple answers here like Using a MutableLiveData instead of LiveData and few other but it still isn't working.
DOA
#Dao
public interface PriceAlertDoa {
public String tableName = DBContract.ALERTS_TABLE_NAME;
#Query("SELECT * FROM " + tableName)
public LiveData<List<PriceAlert>> getAll();
#Query("SELECT * FROM " + tableName + " WHERE " + DBContract.ALERTS_COL_COINID + " LIKE :coinId")
public List<PriceAlert> getByCoinId(String coinId);
#Query("SELECT COUNT(*) FROM " + DBContract.ALERTS_TABLE_NAME)
public int countUsers();
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertAll(PriceAlert... priceAlerts);
#Delete
public void deleteAll(PriceAlert... priceAlert);
#Update
public void updateAll(PriceAlert... priceAlert);
}
Repository
public class PriceAlertRepository {
private PriceAlertDatabase alertsDb;
private Context mContext;
public PriceAlertRepository(Context context){
mContext = context;
if(alertsDb == null){
alertsDb = Room.databaseBuilder(mContext.getApplicationContext(),
PriceAlertDatabase.class, DBContract.ALERTS_TABLE_NAME)
.build();
}
}
public void insertAlert(PriceAlert priceAlert) {
new AsyncTask<Void, Void, Void>(){
#Override
protected Void doInBackground(Void... voids) {
alertsDb.priceAlertDoa().insertAll(priceAlert);
return null;
}
}.execute();
}
public LiveData<List<PriceAlert>> getAllAlerts(){
return alertsDb.priceAlertDoa().getAll();
}
public void deleteAlert(PriceAlert priceAlert){
new AsyncTask<Void, Void, Void>(){
#Override
protected Void doInBackground(Void... voids) {
alertsDb.priceAlertDoa().deleteAll(priceAlert);
return null;
}
}.execute();
}
public void updateAlert(PriceAlert priceAlert){
new AsyncTask<Void, Void, Void>(){
#Override
protected Void doInBackground(Void... voids) {
alertsDb.priceAlertDoa().updateAll(priceAlert);
return null;
}
}.execute();
}
}
ViewModel
public class AlertsViewModel extends AndroidViewModel {
private PriceAlertRepository mAlertRepository;
private LiveData<List<PriceAlert>> mAlertList;
public AlertsViewModel(#NonNull Application application) {
super(application);
mAlertRepository = new PriceAlertRepository(application);
mAlertList = mAlertRepository.getAllAlerts();
}
public LiveData<List<PriceAlert>> getAllAlerts(){
return mAlertList;
}
}
The Fragment displaying List of Alerts
public class AlertsListFragment extends Fragment {
#BindView(R.id.recyclerView_Alerts)
RecyclerView rvAlerts;
#BindView(R.id.button_AddAlert)
Button btnAddAlert;
private Context mContext;
private AlertsViewModel alertsViewModel;
public AlertsListFragment(Context context) {
mContext = context;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_alerts_list, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
rvAlerts.setHasFixedSize(true);
rvAlerts.setLayoutManager(new LinearLayoutManager(mContext));
alertsViewModel = ViewModelProviders.of(this).get(AlertsViewModel.class);
btnAddAlert.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Utils.launchActivity(mContext, AddAlertActivity.class);
}
});
AlertsListAdapter adapter = new AlertsListAdapter(mContext);
rvAlerts.setAdapter(adapter);
alertsViewModel.getAllAlerts().observe(getViewLifecycleOwner(), new Observer<List<PriceAlert>>() {
#Override
public void onChanged(List<PriceAlert> priceAlerts) {
Log.d("LIVE DATA", "Table updated: " + priceAlerts.size());
adapter.updateData(priceAlerts);
}
});
}
}
Thanks in advance.

How to show data from SQLite with CRUD in Android Studio?

Hi I'm doing an app and I made the database but I don't know how to read it to show in a listview for example I have a table CategoriaEntradas and I need to show all the data in a listview but I don't understand very well how it works all the classes
This is my Dao class
#Dao
public interface CategoriaEntradasDao {
#Insert
void insert(CategoriaEntradas categoriaEntradas);
#Update
void update(CategoriaEntradas categoriaEntradas);
#Delete
void delete(CategoriaEntradas categoriaEntradas);
#Query("DELETE FROM CategoriaEntradas")
void deleteAll();
#Query("SELECT * FROM CategoriaEntradas")
LiveData<List<CategoriaEntradas>> getAll();
#Query("SELECT * FROM CategoriaEntradas WHERE IdCategoria =:Id")
LiveData<CategoriaEntradas> getOne(int Id);
}
This is my Entity class
#Entity(tableName = "CategoriaEntradas")
public class CategoriaEntradas {
#PrimaryKey(autoGenerate = true)
#NonNull
#ColumnInfo(name = "IdCategoria")
private Integer _idCategoria;
#NonNull
#ColumnInfo(name = "Name")
private String _name;
#NonNull
#ColumnInfo(name = "Image")
private String _image;
public CategoriaEntradas(#NonNull String name, #NonNull String image) { _name = name; _image = image;}
public void set_idCategoria(Integer _idCategoria){
this._idCategoria = _idCategoria;
}
#NonNull
public Integer getIdCategoria() {
return _idCategoria;
}
#NonNull
public String getName() {
return _name;
}
#NonNull
public String getImage() {
return _image;
}
}
My Repository class
public class CategoriaEntradasRepository {
private CategoriaEntradasDao categoriaEntradasDao;
private LiveData<List<CategoriaEntradas>> listLiveData;
public CategoriaEntradasRepository(Application application) {
Database db = Database.getDatabase(application);
categoriaEntradasDao = db.categoriaEntradasDao();
listLiveData = categoriaEntradasDao.getAll();
}
public LiveData<List<CategoriaEntradas>> getAll() {
return listLiveData;
}
public LiveData<CategoriaEntradas> getOne(int Id) {
return categoriaEntradasDao.getOne(Id);
}
public void insert (CategoriaEntradas categoriaEntradas) {
new CategoriaEntradasRepository.insertAsyncTask(categoriaEntradasDao).execute(categoriaEntradas);
}
private static class insertAsyncTask extends AsyncTask<CategoriaEntradas, Void, Void> {
private CategoriaEntradasDao entradasDao;
insertAsyncTask(CategoriaEntradasDao dao) {
entradasDao = dao;
}
#Override
protected Void doInBackground(final CategoriaEntradas... params) {
entradasDao.insert(params[0]);
return null;
}
}
public void update(final CategoriaEntradas categoriaEntradas){
new CategoriaEntradasRepository.updateAsyncTask(categoriaEntradasDao).execute(categoriaEntradas);
}
private static class updateAsyncTask extends AsyncTask<CategoriaEntradas, Void, Void>{
private CategoriaEntradasDao entradasDao;
updateAsyncTask(CategoriaEntradasDao dao) {
entradasDao = dao;
}
#Override
protected Void doInBackground(final CategoriaEntradas... params){
entradasDao.update(params[0]);
return null;
}
}
public void delete(final CategoriaEntradas categoriaEntradas) {
new CategoriaEntradasRepository.deleteAsyncTask(categoriaEntradasDao).execute(categoriaEntradas);
}
public void delete(final int Id) {
final LiveData<CategoriaEntradas> categoriaEntradas = getOne(Id);
if (categoriaEntradas != null) {
new CategoriaEntradasRepository.deleteAsyncTask(categoriaEntradasDao).execute(categoriaEntradas.getValue());
}
}
private static class deleteAsyncTask extends AsyncTask<CategoriaEntradas, Void, Void>{
private CategoriaEntradasDao entradasDao;
deleteAsyncTask(CategoriaEntradasDao dao) {
entradasDao = dao;
}
#Override
protected Void doInBackground(final CategoriaEntradas... params){
entradasDao.delete(params[0]);
return null;
}
}
}
And my ViewModel class
public class CategoriaEntradasViewModel extends AndroidViewModel {
private CategoriaEntradasRepository repository;
private LiveData<List<CategoriaEntradas>> listLiveData;
public CategoriaEntradasViewModel (Application application) {
super(application);
repository = new CategoriaEntradasRepository(application);
listLiveData = repository.getAll();
}
public LiveData<List<CategoriaEntradas>> getAll() { return listLiveData; }
public LiveData<CategoriaEntradas> getOne(int Id) { return repository.getOne(Id); }
public void insert(CategoriaEntradas categoriaEntradas) { repository.insert(categoriaEntradas); }
public void update(CategoriaEntradas categoriaEntradas){ repository.update(categoriaEntradas);}
public void delete(CategoriaEntradas categoriaEntradas) {repository.delete(categoriaEntradas);}
public void delete(int Id) {repository.delete(Id);}
}
Try this at your Activity or fragment that have your listview.
CategoriaEntradasViewModel mViewModel = ViewModelProviders.of(this).get(CategoriaEntradasViewModel.class);
mViewModel.getAll().observe(this, new Observer<List<CategoriaEntradas>>() {
#Override
public void onChanged(List<CategoriaEntradas> categoriaEntradas) {
adapter = new YourListViewAdapter(this, categoriaEntradas);
listview.setAdapter(adapter);
}
}
Yes, if you have custom list item view. Its similar to array adapter.

Categories

Resources