i have a problem with my code and i can't solve it .
let just a brief of what i'm going to do : i have a address in the ChooseFragment and there is a edit button for editting the address . ok so far good . the edit click is pass new address to the DatabaseRoom and the address text would be changed . but this is happens just for first time . the secound time the address not changed !! . i know that the insert method work and send the new data to the database room but when i want get it with my query (SELECT * FROM ... ) just show the first parameter and not replace with new value . what is wrong with my codes ?
this is my table :
#Entity (tableName = "addresstable")
data class AddressTb(
#PrimaryKey(autoGenerate = true)```
val id : Int? ,
var address: String)```
this is my database :
#Database(entities = [RoomTables::class , AddressTb::class], version = 1, exportSchema = false)
abstract class DataBaseRoom : RoomDatabase() {
abstract fun GetDao(): DaoCart
companion object {
#Volatile
private var instance: DataBaseRoom? = null
private val lock = Any()
operator fun invoke(context: Context) = instance
?: synchronized(lock) {
instance
?: makeDatabase(
context
).also {
instance = it
}
}
private fun makeDatabase(context: Context) = Room.databaseBuilder(
context.applicationContext,
DataBaseRoom::class.java,
"name"
).build()
}
}```
this is my Dao :
```//address table dao
#Query("SELECT * FROM addresstable")
fun getalladress () : LiveData<AddressTb>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertaddress (model : AddressTb)
#Query("DELETE FROM addresstable")
suspend fun deleteaddress ()
this is my Repository :
fun getalladdress() = db.GetDao().getalladress()
suspend fun deleteaddress() = db.GetDao().deleteaddress()
suspend fun insertaddress(model : AddressTb) = db.GetDao().insertaddress(model)
this is my Viewmodel :
fun getaddress() = repository.getalladdress()
fun deleteaddres() = CoroutineScope(Dispatchers.Default).launch {
repository.deleteaddress()
}
fun insertaddress(model : AddressTb) = CoroutineScope(Dispatchers.Default).launch {
repository.insertaddress(model)
this is my fragment where i fetch the new insert :
class ChosseAddress : Fragment() {
lateinit var viewModelRoom: ViewModelRoom
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val vl = inflater.inflate(R.layout.choose_address_layout, container, false)
val database = DataBaseRoom(requireContext())
val repository = RepositoryCart(database)
val factoryRoom = FactoryRoom(repository)
viewModelRoom =
ViewModelProvider(ViewModelStoreOwner { ViewModelStore() }, factoryRoom).get(
ViewModelRoom::class.java
)
viewModelRoom.getaddress().observe(viewLifecycleOwner, Observer {
try {
vl.txt_address.text = it.address
} catch (ex: Exception) {
null
}
})
val animsec: Animation =
AnimationUtils.loadAnimation(vl.context, R.anim.anim_for_btn_zoom_out)
vl.button_back_choose_address.setOnClickListener {
it.startAnimation(animsec)
childFragmentManager.beginTransaction()
.replace(R.id.choose_address_container, HamburgerFragment())
.commit()
}
vl.edit_address.setOnClickListener {
val mycustomview =
LayoutInflater.from(context).inflate(R.layout.alertfialog_costume, null)
val dialogtext = LayoutInflater.from(context).inflate(R.layout.edit_alert_txt, null)
val mBuilder = AlertDialog.Builder(context)
.setView(mycustomview)
.setCustomTitle(dialogtext)
val show = mBuilder.show()
mycustomview.edit_manually.setOnClickListener {
show.dismiss()
childFragmentManager.beginTransaction()
.replace(R.id.choose_address_container, ManuallyAddressFragment())
.commit()
}
}
return vl
}
}```
and this is where i insert data to database :
class ManuallyAddressFragment : Fragment() {
lateinit var viewmodel: ViewModelRoom
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val layoutview = inflater.inflate(R.layout.manually_address_fragment, container, false)
val database = DataBaseRoom(layoutview.context)
val repos = RepositoryCart(database)
val factory = FactoryRoom(repos)
viewmodel = ViewModelProvider(ViewModelStoreOwner { viewModelStore },
factory).get(ViewModelRoom::class.java)
val btncancle: Button = layoutview.btn_cancle
val btnsubmit: Button = layoutview.btn_submit_address
btnsubmit.setOnClickListener {
val edittext = layoutview.edit_address_manually
if (edittext.text.toString().isEmpty()) {
Toast.makeText(context, R.string.submit_btn, Toast.LENGTH_SHORT).show()
} else {
val insert = (AddressTb( null , edittext.text.toString()))
viewmodel.insertaddress(insert)
childFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.anim_fragment_manually,
R.anim.anim_fragment_chooseaddress
)
.replace(R.id.manually_container, ChosseAddress())
.commit()
Toast.makeText(context, "آدرس شما با موفقیت ثبت شد", Toast.LENGTH_SHORT).show()
}
}
btncancle.setOnClickListener {
childFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.anim_fragment_manually,
R.anim.anim_fragment_chooseaddress
)
.replace(R.id.manually_container, ChosseAddress())
.commit()
}
return layoutview
}
}```
i tried so things also use update metohd but the database just back the first parameter and i want the new insert value ...
To update the value use the same id, as in your code I see you try to insert new object AddressTb(null , edittext.text.toString()) you are passing null value for id, pass the same id from the AddressTb object that you wish to update which you get from getalladdress.
For starters, if you're getting all the addresses, it should look like this:
#Query("SELECT * FROM addresstable")
fun getalladress () : LiveData<List<AddressTb>>
To select a single address, use something like this:
#Query("SELECT * FROM addresstable WHERE id= :id")
fun findAddressById(id: Long) : AddressTb
Related
I save the users I brought from the database in the room first. And their favorite status in the database is false. Then when I favorite it, I change its status. Every time I do this the adapter reloads. I used DiffUtil for this.
In addition, I expect the status of the adapter to remain the same after favorites on the Detail page, and if favorites are made on the detail page, it should be reflected on the adapter.
Because of the function I called from viewmodel in UserListFragment, all data is reloaded when fragment occurs.
Fragment
#AndroidEntryPoint
class UserListFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val args: UserListFragmentArgs by navArgs()
val binding = FragmentUserListBinding.inflate(inflater, container, false)
val viewModel = ViewModelProvider(this).get(UserListViewModel::class.java)
(this.activity as AppCompatActivity).supportActionBar?.title = "Github Users"
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
viewModel.userSearch(args.term) //!!!
binding.userListRecyclerView.adapter = UserListAdapter(UserListAdapter.OnClickListener {
val action = it.login.let { login ->
UserListFragmentDirections.actionUserListFragmentToUserDetailFragment(
login!!
)
}
findNavController().navigate(action)
},viewModel)
viewModel.users.observe(viewLifecycleOwner) {
bindRecyclerView(binding.userListRecyclerView, it)
}
return binding.root
}
}
ViewModel
#HiltViewModel
class UserListViewModel #Inject constructor(
private val repository: RoomRepository,
private val firestoreRepository: FirestoreRepository,
private val githubApiService: GithubApiService,
) :
ViewModel() {
private val _users = MutableLiveData<List<UserEntity>?>()
val users: LiveData<List<UserEntity>?>
get() = _users
private val userEntities: MutableList<UserEntity> = mutableListOf()
private val mutableMap: MutableMap<String?, Any?> = mutableMapOf()
fun userSearch(term: String) {
loadFromCache(term)
viewModelScope.launch {
val getPropertiesDeferred = githubApiService.searchUser(term)
try {
val result = getPropertiesDeferred.body()
result?.users?.forEach {
userEntities.add(
UserEntity(
term = term,
login = it.login,
avatar_url = it.avatar_url,
favorite = "no"
)
)
mutableMap.put(term,userEntities)
}
updateSearchResults(userEntities, term)
firestoreRepository.saveSearchResults(mutableMap,term)
} catch (e: Exception) {
Log.e("userListErr", e.message.toString())
}
}
}
fun favorite(login: String){
viewModelScope.launch {
val list = repository.getUserFavoriteStatus(login)
if (list.isNotEmpty()){
if (list[0].favorite == "no"){
repository.addFavorite(login)
}else{
repository.removeFavorite(login)
}
loadFromCache(list[0].term.toString())
}
}
}
private fun updateSearchResults(userEntities: List<UserEntity>, term: String) {
viewModelScope.launch(Dispatchers.IO) {
val favs = repository.getFavorites(term)
repository.insertSearchResults(userEntities)
if (favs.isNotEmpty()){
favs.forEach {
favorite(it.login.toString())
}
}
loadFromCache(term)
}
}
private fun loadFromCache(term: String) {
viewModelScope.launch() {
val list = repository.getSearchResults(term)
if (list.isNotEmpty()){
_users.value = list
}else{
Log.e("boş","boş dürüm")
}
}
}
}
I'm trying to make an Android application using Kotlin, Room Database and both view model and view model factory.
The issue is that when I try to create the object of the entity I am trying to insert, I just get this warning (which i think in this case makes no sense):
Dialog with warning "unexpected tokens (Use ';' to separate expressions on the same line)"
Anyway, here are my Fragment, FragmentViewModel, FragmentViewModelFactory, Entity, Dao and Database files (or relevant parts):
Fragment:
class RegisterFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = DataBindingUtil.inflate<RegisterFragmentBinding>(
inflater,
R.layout.register_fragment,
container,
false
)
val application = requireNotNull(this.activity).application
val dataSource = NbaCafeDB.getInstance(application).usuariDao
val viewModelFactory = RegisterViewModelFactory(dataSource, application)
val registerViewModel =
ViewModelProvider(this, viewModelFactory).get(RegisterViewModel::class.java)
binding.setLifecycleOwner(this)
...
binding.endavantButton.setOnClickListener { View ->
val email = binding.email.text.toString()
val username = binding.registerUser.text.toString()
val password = binding.registerPassword.text.toString()
val confPassword = binding.confirmPassword.text.toString()
if (email != "" && username != "") {
if (registerViewModel.userExists(username)) {
Toast.makeText(context, "Aquest nom d'usuari ja existeix", Toast.LENGTH_LONG)
.show()
} else if (password == confPassword) {
registerViewModel.insert(username, email, password)
} else {
Toast.makeText(context, "Les contrassenyes no coincideixen", Toast.LENGTH_LONG)
.show()
}
}
}
FragmentViewModel:
class RegisterViewModel(
private val dataSource: UsuariDao, application: Application
) : AndroidViewModel(application) {
fun insert(nomUsuari: String, emailUsuari: String, passUsuari: String) {
val usuari: Usuari(nomUsuari, emailUsuari, passUsuari)
viewModelScope.launch {
dataSource.insert(usuari)
}
}
fun userExists(usuariNom: String): Boolean {
return dataSource.userExists(usuariNom)
}
}
FragmentViewModelFactory:
class RegisterViewModelFactory(
private val dataSource: UsuariDao,
private val application: Application
) : ViewModelProvider.Factory {
#Suppress("Unchecked_cast")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(RegisterViewModel::class.java)) {
return RegisterViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown View Model Class")
}
}
Entity:
#Entity
data class Usuari(
#PrimaryKey
#ColumnInfo(name = "nom_usuari")
var nomUsuari: String,
#ColumnInfo(name = "email_usuari")
var emailUsuari: String,
#ColumnInfo(name = "password_usuari")
var passwordUsuari: String
)
DAO:
#Dao
interface UsuariDao {
#Insert
suspend fun insert(usuari: Usuari)
#Query ("SELECT EXISTS(SELECT * FROM Usuari WHERE nomUsuari = :usuariNom)")
fun userExists(usuariNom: String): Boolean
}
And finally, Database:
#Database(
entities =
[Beguda::class,
Comanda::class,
Postre::class,
Sandwich::class,
Usuari::class],
version = 3,
exportSchema = false
)
abstract class NbaCafeDB : RoomDatabase() {
abstract val begudaDao: BegudaDao
abstract val comandaDao: ComandaDao
abstract val postreDao: PostreDao
abstract val sandwichDao: SandwichDao
abstract val usuariDao: UsuariDao
companion object {
#Volatile
private var INSTANCE: NbaCafeDB? = null
fun getInstance(context: Context): NbaCafeDB {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
NbaCafeDB::class.java,
"nba_cafe_database"
)
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
INSTANCE = instance
}
return instance
}
}
}
}
I hope someone has an answer for this, thanks in advance!!
I have two LiveData, aMVoice1, and aMVoice2.
I hope to check if they are equal.
I know I need to use observe to get the value of a LiveData.
so I think isEqual = (mDetailViewModel.aMVoice1.value==mDetailViewMode2.aMVoice1.value ) is wrong.
But I think there are some problems with fun observeVoice(), how can I fix it?
class FragmentDetail : Fragment() {
private lateinit var binding: LayoutDetailBinding
private val mDetailViewModel by lazy {
...
}
var isEqual=false
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
...
binding.lifecycleOwner = this.viewLifecycleOwner
binding.aDetailViewModel=mDetailViewModel
isEqual = (mDetailViewModel.aMVoice1.value==mDetailViewMode2.aMVoice1.value ) // I don't think it's correct.
observeVoice()
return binding.root
}
fun observeVoice() {
mDetailViewModel.aMVoice1.observe(viewLifecycleOwner){value1->
isEqual = (value1==mDetailViewModel.aMVoice2.value) // mDetailViewModel.aMVoice2.value maybe null
}
}
}
class DetailViewModel(private val mDBVoiceRepository: DBVoiceRepository, private val voiceId1:Int,private val voiceId2:Int) : ViewModel() {
val aMVoice1=mDBVoiceRepository.getVoiceById(voiceId1)
val aMVoice2=mDBVoiceRepository.getVoiceById(voiceId2)
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
fun getVoiceById(id:Int)=mDBVoiceDao.getVoiceById(id)
}
#Dao
interface DBVoiceDao{
#Query("SELECT * FROM voice_table where id=:id")
fun getVoiceById(id:Int):LiveData<MVoice>
}
data class MVoice(
#PrimaryKey (autoGenerate = true) #ColumnInfo(name = "id") var id: Int = 0,
var name: String = "",
var path: String = ""
)
Added Content
Is it Ok for the following code?
fun observeVoice() {
mDetailViewModel.aMVoice1.observe(viewLifecycleOwner){value1->
mDetailViewModel.aMVoice2.observe(viewLifecycleOwner){value2->
isEqual = (value1==value2)
}
}
}
According to the official documents, the best way to achieve a solution for such cases is to use MediatorLiveData as a LiveData merger. Using it, you can check the equality of values when a new value is posted on either of LiveDatas:
class DetailViewModel(...) : ViewModel() {
val areMVoicesEqual = MediatorLiveData<Boolean>().apply {
addSource(aMVoice1) { postValue(it == aMVoice2.value) }
addSource(aMVoice2) { postValue(it == aMVoice1.value) }
}
}
Then:
fun observeVoice() {
mDetailViewModel.areMVoicesEqual.observe(viewLifecycleOwner){ equality ->
// do whatever you want with `equality`
}
}
Note that Added Content snippet you mentioned is not correct. In fact, in this case, every time a value is being observed on aMVoice1, a new Observer starts to observe on aMVoice2 which is not right.
I am learning to develop in kotlin with android studio, so I don't have any experience.
I am wanting to insert data into a local database using room database, so far I think I'm doing fine. Now I need to be able to consult those data but I cannot do it, I have searched the internet but I have not been able to solve my problem.
I attach the code.
Class #Entity
class TablasBdApp {
#Entity(tableName = TblConteo.TABLE_NAME)
data class TblConteo(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "linea_id") val Linea_Id: Int = 0,
#ColumnInfo (name = "articulo") val Articulo : String?
)
{
companion object {
const val TABLE_NAME = "TablaConteo"
}
}
}
Class #Dao
#Dao
public interface ItblConteoDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertArticulo(taskTblConteo: TablasBdApp.TblConteo);
#Query("SELECT * FROM " + TablasBdApp.TblConteo.TABLE_NAME + " ORDER BY Linea_Id desc")
fun getConteoArticulos(): LiveData<List<TablasBdApp.TblConteo>>
}
Class #DataBase
class BaseDeDatos {
#Database(entities = [TablasBdApp.TblConteo::class], version = 1)
abstract class PortatilDataBase : RoomDatabase() {
abstract fun itblconteoDao () : ItblConteoDao
companion object {
private const val DATABASE_NAME = "portatildb"
#Volatile
private var INSTANCE: PortatilDataBase? = null
fun getInstance(context: Context): PortatilDataBase? {
INSTANCE ?: synchronized(this) {
INSTANCE = Room.databaseBuilder(
context.applicationContext,
PortatilDataBase::class.java,
DATABASE_NAME
).build()
}
return INSTANCE
}
}
}
}
Class #Repository
class ConteoRepository(application: Application) {
private val itblConteoDao: ItblConteoDao? = BaseDeDatos.PortatilDataBase.getInstance(application)?.itblconteoDao()
fun insert (tblconteo: TablasBdApp.TblConteo){
if(itblConteoDao != null) InsertAsyncTask(itblConteoDao).execute(tblconteo)
}
fun getConteo(): LiveData<List<TablasBdApp.TblConteo>> {
return itblConteoDao?.getConteoArticulos() ?: MutableLiveData<List<TablasBdApp.TblConteo>>()
}
private class InsertAsyncTask(private val itblConteoDao: ItblConteoDao) :
AsyncTask<TablasBdApp.TblConteo, Void, Void>() {
override fun doInBackground(vararg tblconteos: TablasBdApp.TblConteo?): Void? {
for (tblconteo in tblconteos) {
if (tblconteo != null) itblConteoDao.insertArticulo(tblconteo)
}
return null
}
}
}
Class #CustomAdapter
class CustomAdapter(context: Context): BaseAdapter() {
private val mContext: Context
init{
mContext = context
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
//val repository = ConteoRepository(application = Application())
var textView = TextView(mContext)
val observer = Observer<List<TablasBdApp.TblConteo>> { conteos ->
if (conteos != null) {
var text = ""
for (conteo in conteos) {
text += conteo.Linea_Id.toString() + " " + conteo.Articulo
}
textView.text = text
}
}
//repository.getConteo().observe(this, observer)
return textView
}
override fun getItem(position: Int): Any {
return "Test Articulo"
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
TODO("Not yet implemented")
}
}
Class #Fragment
class CapturaConteoFragment() : Fragment() {
private lateinit var capturaConteoViewModel: CapturaConteoViewModel
#SuppressLint("ResourceType")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
capturaConteoViewModel =
ViewModelProviders.of(this).get(CapturaConteoViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_capturaconteo, container, false)
//val textView: TextView = root.findViewById(R.id.text_capturaconteo)
capturaConteoViewModel.text.observe(viewLifecycleOwner, Observer {
//textView.text = it
val botonGuardar: Button = root.findViewById(R.id.btn_guardar)
val textoArticulo : EditText = root.findViewById(R.id.edit_art_upc)
var listview: ListView = root.findViewById(R.id.list_conteo)
botonGuardar.setOnClickListener {
if(textoArticulo.getText().toString().trim().isEmpty()){
Toast.makeText(activity, "Captura un articulo", Toast.LENGTH_LONG).show()
textoArticulo.requestFocus()
}
else{
saveConteo(TablasBdApp.TblConteo(Articulo = textoArticulo.text.trim().toString()))
Toast.makeText(activity, "Articulo guardado", Toast.LENGTH_LONG).show()
//var listaconteo = arrayOf(muestraConteo())
var prodAdapter = CustomAdapter(**this**) **<- This is where it marks error.**
listview?.adapter = prodAdapter
}
}
})
return root
}
private fun saveConteo(tblConteo: TablasBdApp.TblConteo) {
val repository = ConteoRepository(application = Application())
repository.insert(tblConteo)
val conteo = repository.getConteo()
}
private fun showConteo (){
val repository = ConteoRepository(application = Application())
repository.getConteo()
}
}
Thanks for your help.
Regards.
In your DAO you are returning LiveData that means you need to observe it and best practice would be through ViewModel which implies you should MVVM architecture, there are plenty of tutorials and courses on it.
Here is something from google Codelabs
Other way would would be to return just list rather than live data of a list. This is bad practice because you will have to refresh it all the time and do a lot of extra work.
PS your adapter needs fixing too my suggestion is follow codelabs or watch complete tutorial on Room + MVVM.
Hope this helps
I am trying, without success, to solve a problem for days. I would like to update my recyclerView whenever the records of a particular model change in the Database (DB Room). I use ViewModel to handle the model data and the list of records are stored in LiveData.
Database
#Database(entities = arrayOf(Additive::class), version = ElementDatabase.DB_VERSION, exportSchema = false)
abstract class ElementDatabase() : RoomDatabase() {
companion object {
const val DB_NAME : String = "element_db"
const val DB_VERSION : Int = 1
fun get(appContext : Context) : ElementDatabase {
return Room.databaseBuilder(appContext, ElementDatabase::class.java, DB_NAME).build()
}
}
abstract fun additivesModels() : AdditiveDao
}
Model
#Entity
class Additive {
#PrimaryKey #ColumnInfo(name = "id")
var number : String = ""
var dangerousness : Int = 0
var description : String = ""
var names : String = ""
var notes : String = ""
var risks : String = ""
var advice : String = ""
}
Dao
#Dao
interface AdditiveDao {
#Query("SELECT * FROM Additive")
fun getAllAdditives() : LiveData<List<Additive>>
#Query("SELECT * FROM Additive WHERE id = :arg0")
fun getAdditiveById(id : String) : Additive
#Query("DELETE FROM Additive")
fun deleteAll()
#Insert(onConflict = REPLACE)
fun insert(additive: Additive)
#Update
fun update(additive: Additive)
#Delete
fun delete(additive: Additive)
}
ViewModel
class AdditiveViewModel(application: Application) : AndroidViewModel(application) {
private var elementDatabase : ElementDatabase
private val additivesModels : LiveData<List<Additive>>
init {
this.elementDatabase = ElementDatabase.get(appContext = getApplication())
this.additivesModels = this.elementDatabase.additivesModels().getAllAdditives()
}
fun getAdditivesList() : LiveData<List<Additive>> {
return this.additivesModels
}
fun deleteItem(additive : Additive) {
DeleteAsyncTask(this.elementDatabase).execute(additive)
}
private class DeleteAsyncTask internal constructor(private val db: ElementDatabase) : AsyncTask<Additive, Void, Void>() {
override fun doInBackground(vararg params: Additive): Void? {
db.additivesModels().delete(params[0])
return null
}
}
}
Fragment
class AdditivesFragment : LifecycleFragment() {
private var viewModel : AdditiveViewModel? = null
private var adapter : AdditivesAdapter? = null
companion object {
fun newInstance() : AdditivesFragment {
val f = AdditivesFragment()
val args = Bundle()
f.arguments = args
return f
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater?.inflate(R.layout.fragment_additives, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
this.adapter = AdditivesAdapter(ArrayList<Additive>())
this.additives_list.layoutManager = GridLayoutManager(this.context, 2, GridLayoutManager.VERTICAL, false)
this.additives_list.adapter = this.adapter
this.viewModel = ViewModelProviders.of(this).get(AdditiveViewModel::class.java)
this.viewModel?.getAdditivesList()?.observe(this, Observer<List<Additive>> { additivesList ->
if(additivesList != null) {
this.adapter?.addItems(additivesList)
}
})
super.onActivityCreated(savedInstanceState)
}
}
Now, my question is why is the observer called only once (at the start of the fragment) and then is not called back again? How can I keep the observer constantly listening to the changes in the DB (insert, update, delete) so that my recyclerView instantly can be updated? Thanks a lot for any suggestion.
This is where you made a mistake:
this.viewModel = ViewModelProviders.of(this).get(AdditiveViewModel::class.java)
you are passing this while you are inside the fragment which is pretty disturbing for some people cause it is not a syntax error but logical. You have to pass activity!! instead, it will be like this:
this.viewModel = ViewModelProviders.of(activity!!).get(AdditiveViewModel::class.java)
UPDATE:
Pass viewLifecycleOwner while being inside fragment while observing the Data
mainViewModel.data(viewLifecycleOwner, Observer{})
If you're using fragmentKtx, you can init viewModel this way:
private val viewModel by viewModels<MainViewModel>()
If You've viewModelFactory:
private val viewModel by viewModels<MainViewModel>{
viewModelFactory
}
with this approach you don't need to call:
// you can omit this statement completely
viewModel = ViewModelProviders.of(this).get(AdditiveViewModel::class.java)
You can simply just start observing the data..