Reputation: 119
I am new to Android Studio development and most of it is straight-forward. It seems these concepts are fairly new and Google's documentation for them are either poorly written or just confusing me greatly. Even looking on here at others questions hasn't been sufficient to clear this up for me.
What I have so far in my app is a user either registers or logs in where the character of the user gets saved into the database or is retrieved from database respectfully. At the current state the login and registration work with the database inserting and retrieving their character update the UI accordingly.
I have fragments that alter the characters stats and the UI does not get updated after changes using an observer on the character using the ViewModel. Also, the database is not updated with the changes either.
There are probably simple things I am missing to get this to work. Please let me know of any alterations I should make to the current code and any advice moving forward.
The main objective I am trying to do is save the characters changes to the database and have the UI update with the new changes.
EDIT: I have removed the callback logic from the database, refactored some of my Dao queries and updated the repo and viewmodel accordingly, and added a findById query. I have added databinding to my home fragment's xml using my character class.
My database:
@Database(entities = {Character.class}, version = 1)
public abstract class MyDatabase extends RoomDatabase {
public abstract UserDao userDao();
private static MyDatabase INSTANCE;
public static MyDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (MyDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
MyDatabase.class, "character_database")
.build();
}
}
}
return INSTANCE;
}
}
My DAO:
@Dao //Data Access Object
public interface UserDao {
@Query("SELECT * FROM character_table")
LiveData<List<Character>> getAllCharacters();
@Query("SELECT * FROM character_table WHERE email LIKE :email LIMIT 1")
LiveData<Character> findByEmail(String email);
@Query("SELECT * FROM character_table WHERE name")
LiveData<List<Character>> sortByName();
@Query("SELECT * FROM character_table WHERE id LIKE :id LIMIT 1")
LiveData<Character> findById(int id);
@Query("SELECT * FROM character_table WHERE rank")
LiveData<List<Character>> sortByRank();
@Query("SELECT * FROM character_table WHERE total_exp")
LiveData<List<Character>> sortByExp();
@Query("SELECT * FROM character_table WHERE village")
LiveData<List<Character>> sortByVillage();
@Query("SELECT * FROM character_table WHERE email LIKE :email AND password LIKE :password")
LiveData<Character> login(String email, String password);
@Delete
void deleteCharacter(Character player);
@Query("DELETE FROM character_table")
void deleteAll();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void save(Character player);
@Update
void update(Character player);
My repository: EDIT: I added asynctasks to update and delete.
public class CharacterRepository {
private final UserDao userDao;
private LiveData<List<Character>> allCharacters;
private LiveData<List<Character>> sortByName;
private LiveData<List<Character>> sortByExp;
private LiveData<List<Character>> sortByRank;
private LiveData<List<Character>> sortByVillage;
CharacterRepository(Application application){
MyDatabase db = MyDatabase.getDatabase(application);
userDao = db.userDao();
allCharacters = userDao.getAllCharacters();
sortByName = userDao.sortByName();
sortByExp = userDao.sortByExp();
sortByRank = userDao.sortByRank();
sortByVillage = userDao.sortByVillage();
}
public LiveData<Character> login(String email, String password){
return userDao.login(email, password);
}
LiveData<List<Character>> sortByName(){
return sortByName;
}
LiveData<Character> findByEmail(String email){
return userDao.findByEmail(email);
}
LiveData<List<Character>> sortByName(){
return sortByName;
}
LiveData<List<Character>> sortByExp(){
return sortByExp;
}
LiveData<List<Character>> sortByRank(){
return sortByRank;
}
LiveData<List<Character>> sortByVillage(){
return sortByVillage;
}
LiveData<List<Character>> getAll(){
return allCharacters;
}
public void insert(Character player){
new insertAsyncTask(userDao).execute(player);
}
public void update(Character player){
new updateAsyncTask(userDao).execute(player);
}
public void delete(Character player){
new deleteAsyncTask(userDao).execute(player);
}
public LiveData<Character> getPlayer(String id){
return userDao.findByEmail(id);
}
private static class insertAsyncTask extends AsyncTask<Character, Void, Void> {
private UserDao mAsyncTaskDao;
insertAsyncTask(UserDao dao) {
mAsyncTaskDao = dao;
}
@Override
protected Void doInBackground(Character... characters) {
mAsyncTaskDao.save(characters[0]);
return null;
}
}
private static class updateAsyncTask extends AsyncTask<Character, Void, Void> {
private UserDao mAsyncTaskDao;
updateAsyncTask(UserDao dao) {
mAsyncTaskDao = dao;
}
@Override
protected Void doInBackground(Character... characters) {
mAsyncTaskDao.update(characters[0]);
return null;
}
}
private static class deleteAsyncTask extends AsyncTask<Character, Void, Void> {
private UserDao mAsyncTaskDao;
deleteAsyncTask(UserDao dao) {
mAsyncTaskDao = dao;
}
@Override
protected Void doInBackground(Character... characters) {
mAsyncTaskDao.deleteCharacter(characters[0]);
return null;
}
}
}
My ViewModel: EDIT: getPlayer is found by id instead of email.
public class MyViewModel extends AndroidViewModel {
private CharacterRepository cRepository;
private LiveData<List<Character>> allCharacters;
public MyViewModel(Application application){
super(application);
cRepository = new CharacterRepository(application);
allCharacters = cRepository.getAll();
}
LiveData<List<Character>> getAllCharacters() {return allCharacters;}
public void insert(Character player){
cRepository.insert(player);
}
public void deletePlayer(Character player){
cRepository.delete(player);
}
public void updatePlayer(Character player){
cRepository.update(player);
}
public LiveData<Character> getPlayer(int id){
return cRepository.getPlayer(id);
}
public LiveData<Character> findByEmail(String email){
return cRepository.findByEmail(email);
}
public LiveData<Character> login(String email, String password){
return cRepository.login(email, password);
}
}
This code is my home fragment with the databinding added: EDIT: Used getArguments to get the Id from my activity, called binding.setPlayer(player) inside onChanged() makes everything work properly updating the database and the UI. After I set something for the player I update the player
private MyViewModel viewModel;
private FragmentHomeBinding binding;
private View rootView;
private Character player;
private int id;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false);
rootView = binding.getRoot();
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
id = getArguments().getInt("id");
return rootView;
}
@Override
public void onStart() {
super.onStart();
// Home fragment buttons.
final Button sleepButton = getView().findViewById(R.id.sleepButton);
final Button bankButton = getView().findViewById(R.id.bankButton);
final Button infoButton = getView().findViewById(R.id.infoButton);
// The view that shows the players pool ratio.
info = getView().findViewById(R.id.poolAmount);
layout = getView().findViewById(R.id.poolInfo);
// The players status bars.
healthBar = getView().findViewById(R.id.healthBar);
chakraBar = getView().findViewById(R.id.chakraBar);
staminaBar = getView().findViewById(R.id.staminaBar);
//Observe LiveData Character.
viewModel.getPlayer(id).observe(this, new Observer<Character>() {
@Override
public void onChanged(@Nullable final Character character) {
player = character;
player.setRank(updateRank());
binding.setPlayer(player);
//Setting the progress and max for each user pool.
healthBar.setProgress((int)player.getHealth());
healthBar.setMax((int)player.getHealthMax());
chakraBar.setProgress((int)player.getChakra());
chakraBar.setMax((int)player.getChakraMax());
staminaBar.setProgress((int)player.getStamina());
staminaBar.setMax((int)player.getStaminaMax());
}
});
sleepButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if (player.isAwake()) {
player.setAwake(false);
sleepButton.setText("Wake Up");
Toast.makeText(getContext(), "You went to sleep...", Toast.LENGTH_SHORT).show();
} else {
player.setAwake(true);
sleepButton.setText("Sleep");
Toast.makeText(getContext(), "You woke up!", Toast.LENGTH_SHORT).show();
}
}
});
bankButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
final View bankPrompt = getLayoutInflater().inflate(R.layout.bank_prompt, null);
builder.setView(bankPrompt);
final AlertDialog dialog = builder.create();
dialog.show();
TextView bankMoney = bankPrompt.findViewById(R.id.bankAmount);
TextView pocketMoney = bankPrompt.findViewById(R.id.pocketAmount);
Button depositButton = bankPrompt.findViewById(R.id.depositButton);
Button withdrawButton = bankPrompt.findViewById(R.id.withdrawButton);
ImageButton closeBankPrompt = bankPrompt.findViewById(R.id.closeBank);
pocketMoney.setText(String.valueOf(player.getPocketMoney()));
bankMoney.setText(String.valueOf(player.getBankMoney()));
depositButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final View transactionPrompt = getLayoutInflater()
.inflate(R.layout.bank_transaction, null);
builder.setView(transactionPrompt);
TextView transactionText = transactionPrompt.findViewById(R.id.bankTransactionText);
transactionText.setText(R.string.bank_deposit);
final AlertDialog dialog = builder.create();
dialog.show();
Button confirmDeposit = transactionPrompt.findViewById(R.id.confirmTransaction);
ImageButton closePrompt = transactionPrompt.findViewById(R.id.cancelTransaction);
confirmDeposit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bankTransaction(player,0, transactionPrompt, bankPrompt);
dialog.hide();
}
});
closePrompt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.hide();
}
});
}
});
withdrawButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final View transactionPrompt = getLayoutInflater()
.inflate(R.layout.bank_transaction, null);
builder.setView(transactionPrompt);
TextView transactionText = transactionPrompt.findViewById(R.id.bankTransactionText);
transactionText.setText(R.string.bank_withdraw);
final AlertDialog dialog = builder.create();
dialog.show();
Button confirmWithdraw = transactionPrompt.findViewById(R.id.confirmTransaction);
ImageButton closePrompt = transactionPrompt.findViewById(R.id.cancelTransaction);
confirmWithdraw.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bankTransaction(player,1, transactionPrompt, bankPrompt);
dialog.hide();
}
});
closePrompt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.hide();
}
});
}
});
closeBankPrompt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.hide();
}
});
}
});
infoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ScrollView info = getView().findViewById(R.id.infoView);
if (info.getVisibility() == View.VISIBLE)
info.setVisibility(View.GONE);
else
info.setVisibility(View.VISIBLE);
}
});
}
I have the same observe line in my second fragment with similar actions as this one. They function properly the UI just isn't updated after the first time. I feel like I have the wrong approach about the observer and how its supposed to be called. EDIT: I did the same in my second fragment now and it also works.
Upvotes: 5
Views: 2136
Reputation: 119
I figured out how it works now and will update the code accordingly. Maybe someone will find it useful.
Upvotes: 3