I have an editText and a button. When I click on the button the data from the edittext gets inserted into the database. On the save activity I have recyclerView which will show the items getting from the viewModel. I have put the observer to observer the data but it is not showing any item in the recuclerView.
class MainActivity : AppCompatActivity(), NoteAdapter.OnItemClicked {
private lateinit var recyclerView: RecyclerView
private lateinit var btnAdd: Button
private lateinit var etNote: EditText
lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
btnAdd = findViewById(R.id.btn_add)
etNote = findViewById(R.id.et_note)
val mAdapter = NoteAdapter(this)
recyclerView.apply {
adapter = mAdapter
layoutManager = LinearLayoutManager(this#MainActivity)
}
viewModel = ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application))[MyViewModel::class.java]
viewModel.allNotes.observe(this, Observer {note->
note?.let {
mAdapter.submitList(it)
}
})
btnAdd.setOnClickListener {
addNote()
}
}
override fun onClick(note: Notes) {
viewModel.delete(note)
}
private fun addNote(){
val note: String = etNote.text.toString()
if(note.isNotEmpty()) {
viewModel.insert(Notes(note))
Toast.makeText(this, "$note inserted", Toast.LENGTH_SHORT).show()
}
}
}
Adapter Class
class NoteAdapter(private val clickListener: OnItemClicked)
: ListAdapter<Notes, NoteAdapter.MyViewHolder>(NoteComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.note_item, parent, false)
val viewHolder = MyViewHolder(view)
viewHolder.ivDelete.setOnClickListener{
clickListener.onClick(getItem(viewHolder.adapterPosition))
}
return viewHolder
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentNote = getItem(position)
holder.apply {
tvNoteItem.text = currentNote.note
}
}
interface OnItemClicked{
fun onClick(note: Notes)
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvNoteItem: TextView = itemView.findViewById(R.id.tv_note)
val ivDelete: ImageView = itemView.findViewById(R.id.iv_delete)
}
class NoteComparator : DiffUtil.ItemCallback<Notes>(){
override fun areItemsTheSame(oldItem: Notes, newItem: Notes): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Notes, newItem: Notes): Boolean {
return oldItem == newItem
}
}
}
ViewModel Class
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val repository: NotesRepository
val allNotes : LiveData<List<Notes>>
init {
val dao = NoteDatabase.getDatabase(application).noteDao()
repository = NotesRepository(dao)
allNotes = repository.noteList
}
fun delete(note: Notes) = viewModelScope.launch(Dispatchers.IO) {
repository.delete(note)
}
fun insert(note: Notes) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(note)
}
}
Heey Divyansh,
Analyzing your code I believe the problem is in your ViewModel. You are trying to change the property allNotes, which is a LiveData. You need mutable live data, so declare it like private because we can't expose a mutable property. And your allNotes property will receive the changes from this mutable property, like this:
class MyViewModel: ViewModel() {
private val _allNotes = MutableLiveData<List<Notes>>(emptyList())
val allNotes: LiveData<List<Notes>>
get() = _allNotes
}
Then, update the mutable live data:
class MyViewModel: ViewModel() {
// ...
init {
val dao = NoteDatabase.getDatabase(application).noteDao()
repository = NotesRepository(dao)
_allNotes.value = repository.noteList
}
}
I have found why it is not displaying the items because the viewModel is not inserting the the data as I have used Dispatcher.IO in the viewModel scope.
I removed it and now it is inserting and showing the items.
Related
I have an edit text and a button. If I click on the button the data of the edit text gets inserted into my List which a live data. I want to show the items of the list in the recycler view but its not showing any data. I think the observer is not getting called as it is showing greyish (same as when we declare a variable or function and we don't use it) or may be there is something else happening.
This is the only activity class
class MainActivity : AppCompatActivity(), NoteAdapter.OnItemClicked {
private lateinit var recyclerView: RecyclerView
private lateinit var btnAdd: Button
private lateinit var etNote: EditText
lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
btnAdd = findViewById(R.id.btn_add)
etNote = findViewById(R.id.et_note)
val mAdapter = NoteAdapter(this, this)
recyclerView.apply {
adapter = mAdapter
layoutManager = LinearLayoutManager(this#MainActivity)
}
viewModel = ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application))[MyViewModel::class.java]
viewModel.allNotes.observe(this, Observer {
it?.let {
mAdapter.updateList(it)
}
})
btnAdd.setOnClickListener {
addNote()
}
}
override fun onClick(note: Notes) {
viewModel.delete(note)
}
private fun addNote(){
val note: String = etNote.text.toString()
if(note.isNotBlank()) {
viewModel.insert(Notes(note))
Toast.makeText(this, "$note inserted", Toast.LENGTH_SHORT).show()
}
}
}
And this is my adapter
class NoteAdapter(private val context: Context, private val clickListener: OnItemClicked) : RecyclerView.Adapter<NoteAdapter.MyViewHolder>() {
private val allNotes = mutableListOf<Notes>()
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvNoteItem: TextView = itemView.findViewById(R.id.tv_note)
val ivDelete: ImageView = itemView.findViewById(R.id.iv_delete)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.note_item, parent, false)
val viewHolder = MyViewHolder(view)
viewHolder.ivDelete.setOnClickListener{
clickListener.onClick(allNotes[viewHolder.adapterPosition])
}
return viewHolder
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentNote = allNotes[position]
holder.apply {
tvNoteItem.text = currentNote.note
}
}
override fun getItemCount() = allNotes.size
interface OnItemClicked{
fun onClick(note: Notes)
}
fun updateList(newList: List<Notes>){
allNotes.clear()
allNotes.addAll(newList)
notifyDataSetChanged()
}
}
#Entity(tableName = "notes_table")
data class Notes(val note: String) {
#PrimaryKey(autoGenerate = true)
var id = 0
}
Dao:
#Dao
interface NotesDao {
#Insert
suspend fun insertNote(note: Notes){ //Suspended means it will run it background thread
}
#Delete
suspend fun deleteNotes(note: Notes){
}
#Query("Select * from notes_table order by id")
fun getAllNotes(): LiveData<List<Notes>>
}
Database:
#Database(entities = arrayOf(Notes::class), version = 1, exportSchema = false)
abstract class NoteDatabase : RoomDatabase() {
abstract fun noteDao() : NotesDao
companion object{
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
private var INSTANCE: NoteDatabase? = null
fun getDatabase(context: Context): NoteDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
NoteDatabase::class.java,
"notes_table"
).build()
INSTANCE = instance
// return instance
instance
}
}
}
}
This is viewModel class
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val repository: NotesRepository
val allNotes : LiveData<List<Notes>>
init {
val dao = NoteDatabase.getDatabase(application).noteDao()
repository = NotesRepository(dao)
allNotes = repository.noteList
}
fun delete(note: Notes) = viewModelScope.launch(Dispatchers.IO) {
repository.delete(note)
}
fun insert(note: Notes) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(note)
}
}
Here is my repository class
class NotesRepository(private val dao: NotesDao) {
val noteList = dao.getAllNotes()
suspend fun insert(note: Notes){
dao.insertNote(note)
}
suspend fun delete(note: Notes){
dao.deleteNotes(note)
}
}
I am fetching JSON data from API and passing it in recycler view but if I want to fetch new data and display it in recycler view then I have to clear the list and then add new data in that list and notify the adapter that the data is changed but it is not updated what should I do?
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var recipeViewModel: RecipeViewModel
private lateinit var mainBinding: ActivityMainBinding
private lateinit var recipeAdapter: RecipeAdapter
private lateinit var recipeItemList: ArrayList<Hit>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
recipeViewModel =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory
.getInstance(application)
)[RecipeViewModel::class.java]
recipeItemList = arrayListOf()
mainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
mainBinding.recyclerView.hasFixedSize()
recipeAdapter = RecipeAdapter(this)
mainBinding.recyclerView.adapter = recipeAdapter
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
recipeItemList.addAll(recipeItems.hits)
recipeAdapter.updateRecipes(recipeItemList)
Log.d("RESPONSE", recipeItems.toString())
Log.d("List size", recipeAdapter.itemCount.toString())
})
searchRecipeName()
}
private fun searchRecipeName() {
mainBinding.searchRecipeFabBtn.setOnClickListener {
val view = layoutInflater.inflate(R.layout.recipe_search_layout, null)
val searchRecipeET = view.findViewById<EditText>(R.id.searchRecipeET)
val searchRecipeBtn = view.findViewById<Button>(R.id.searchRecipeBtn)
val bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.apply {
this.setContentView(view)
this.show()
}
searchRecipeBtn.setOnClickListener {
val recipeName = searchRecipeET.text.toString()
searchRecipeName(recipeName, searchRecipeET, bottomSheetDialog)
}
}
}
private fun searchRecipeName(
recipeName: String,
searchRecipeET: EditText,
bottomSheetDialog: BottomSheetDialog
) {
if (recipeName.isEmpty()) {
searchRecipeET.error = "Please enter recipe name"
} else {
recipeViewModel.getRecipes(recipeName)
bottomSheetDialog.dismiss()
}
}
}
RecipeAdapter.kt
class RecipeAdapter(val context: Context) : RecyclerView.Adapter<RecipeAdapter.RecipeViewHolder>() {
private val recipesList: ArrayList<Hit> = arrayListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.recipe_items_layout, null, false)
return RecipeViewHolder(view)
}
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val currentItem = recipesList[position]
holder.recipeImageView.load(currentItem.recipe.image)
holder.recipeNameText.text = currentItem.recipe.label
}
override fun getItemCount(): Int {
return recipesList.size
}
class RecipeViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val recipeImageView: ImageView = itemView.findViewById(R.id.recipeImageView)
val recipeNameText: TextView = itemView.findViewById(R.id.recipeNameText)
}
fun updateRecipes(newRecipesList: ArrayList<Hit>){
recipesList.clear()
Log.d("RECIPE SIZE", "${recipesList.size}")
recipesList.addAll(newRecipesList)
notifyDataSetChanged()
}
}
This may be helpful.
Be careful of this :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
recipeViewModel =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory
.getInstance(application)
)[RecipeViewModel::class.java]
recipeItemList = arrayListOf()
mainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
mainBinding.recyclerView.hasFixedSize()
recipeAdapter = RecipeAdapter(this)
mainBinding.recyclerView.adapter = recipeAdapter
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
// You're adding items here but never clear the list
// list will be bigger every time you'll be notified
// recipeItemList.addAll(recipeItems.hits
// recipeAdapter.updateRecipes(recipeItemList)
// Do this instead
recipeItemList = recipeItems.hits
recipeAdapter.updateRecipes(recipeItemList)
Log.d("RESPONSE", recipeItems.toString())
Log.d("List size", recipeAdapter.itemCount.toString())
})
searchRecipeName()
}
Also, here: It's a little better to do this (https://stackoverflow.com/a/10298038/4221943)
fun updateRecipes(newRecipesList: ArrayList<Hit>){
recipesList = newRecipesList
Log.d("RECIPE SIZE", "${recipesList.size}")
notifyDataSetChanged()
}
BTW it will always be more efficient to use the more specific change events if you can. Rely on notifyDataSetChanged() as a last resort. It is also good practice to use notifyItemInserted(mItems.size() - 1) for "easier" solution.
You could convert the RecyclerView.Adapter into a ListAdapter:
class RecipeAdapter(val context: Context) : ListAdapter<Hit, RecipeAdapter.RecipeViewHolder>(RecipeDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.recipe_items_layout, null, false)
return RecipeViewHolder(view)
}
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val currentItem = getItem(position)
holder.recipeImageView.load(currentItem.recipe.image)
holder.recipeNameText.text = currentItem.recipe.label
}
class RecipeViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val recipeImageView: ImageView = itemView.findViewById(R.id.recipeImageView)
val recipeNameText: TextView = itemView.findViewById(R.id.recipeNameText)
}
}
class RecipeDiffCallback : DiffUtil.ItemCallback<Hit>() {
// Change this condition based on the attribute of `Hit` that will change
override fun areItemsTheSame(oldItem: Hit, newItem: Hit): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Hit, newItem: Hit): Boolean = oldItem == newItem
}
Then update its content with the submitList method.
Every item not satisfying the RecipeDiffCallback conditions will be automatically updated:
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
recipeAdapter.submitList(recipeItems.hits)
})
The data in the RecyclerView is called the first time without issues. However when i refresh the data, for some reason all the items goes blank.
The MainActivity is this
class BusinessActivity : AppCompatActivity() {
private val businessViewModel: BusinessViewModel by viewModel()
private val imageLoader: ImageLoader by inject()
private lateinit var staggeredGridLayoutManager: StaggeredGridLayoutManager
private lateinit var skeleton: Skeleton
private val adapter: BusinessAdapter by lazy { BusinessAdapter(imageLoader, businessViewModel) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_business)
initToolbar()
skeleton = findViewById<SkeletonLayout>(R.id.skeletonLayout)
staggeredGridLayoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
staggeredGridLayoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
recycler_view.apply {
layoutManager = staggeredGridLayoutManager
adapter = this#BusinessActivity.adapter
setHasFixedSize(true)
}
setupSkeleton()
initializeObserverBusiness()
refreshBusiness.setOnRefreshListener {
refreshBusiness.isRefreshing = true
skeleton.showSkeleton()
businessViewModel.retrieveBusiness()
}
}
private fun initToolbar() {
setSupportActionBar(toolbar)
supportActionBar?.title = getString(R.string.app_name)
this.setSystemBarColor(this)
}
private fun setupSkeleton(){
skeleton = recycler_view.applySkeleton(R.layout.business_card, 6)
skeleton.showSkeleton()
}
private fun initializeObserverBusiness(){
businessViewModel.uiState.observe(this, Observer {
val dataState = it ?: return#Observer
if (!dataState.showProgress){
refreshBusiness.isRefreshing = false
skeleton.showOriginal()
}
if (dataState.business != null && !dataState.business.consumed){
dataState.business.consume()?.let { business ->
adapter.submitList(business)
}
}
if (dataState.error != null && !dataState.error.consumed){
dataState.error.consume()?.let { error ->
Toast.makeText(this, resources.getString(error), Toast.LENGTH_LONG).show()
}
}
})
}
}
and the Adapter for the RecyclerView, im currently using DiffCallback and ListAdapter due to a better performance.
class BusinessAdapter(var imageLoader: ImageLoader, var viewModel: BusinessViewModel) : ListAdapter<Business, BusinessViewHolder>(DIFF_CALLBACK){
companion object{
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Business>() {
override fun areItemsTheSame(oldItem: Business, newItem: Business) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Business, newItem: Business) = oldItem == newItem
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = BusinessViewHolder.create(parent)
override fun onBindViewHolder(holder: BusinessViewHolder, position: Int) {
holder.bind(getItem(position), imageLoader, viewModel)
}
}
and the ViewHolder for the Adapter
class BusinessViewHolder constructor(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(business: Business, imageLoader: ImageLoader, viewModel: BusinessViewModel) {
businessImage?.let { imageLoader.load("${BuildConfig.MY_URL}/gallery/${business.images[0]}", it) }
ownerBusiness.text = business.owner
businessName.text = business.name
cardBusiness.setOnClickListener {
viewModel.callDetailBusiness(business.id)
}
}
companion object {
fun create(parent: ViewGroup): BusinessViewHolder {
return BusinessViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.business_card, parent, false))
}
}
}
and the ViewModel
class BusinessViewModel (private val businessRepository: BusinessRepository): ViewModel() {
private val _uiState = MutableLiveData<BusinessDataState>()
val uiState: LiveData<BusinessDataState> get() = _uiState
val _showDetailBusiness = MutableLiveData<Int?>()
val showDetailBusiness: LiveData<Int?> get() = _showDetailBusiness
init {
retrieveBusiness()
}
fun retrieveBusiness(){
viewModelScope.launch {
runCatching {
emitUiState(showProgress = true)
businessRepository.retrieveBusiness()
}.onSuccess {
emitUiState(business = Event(it))
}.onFailure {
emitUiState(error = Event(R.string.internet_failure_error))
}
}
}
fun callDetailBusiness(businessId: Int) {
_showDetailBusiness.value = businessId
}
private fun emitUiState(showProgress: Boolean = false, business: Event<List<Business>>? = null, error: Event<Int>? = null){
val dataState = BusinessDataState(showProgress, business, error)
_uiState.value = dataState
}
data class BusinessDataState(val showProgress: Boolean, val business: Event<List<Business>>?, val error: Event<Int>?)
}
When the data is loaded for the first time i see this.
however when i apply the SwipeRefresh. I receive the data.
D/OkHttp: [{"id":18,"name":"Whatsup","owner":"Mi
Soledad","category":"ToDo",
but the RecyclerView won't attach the new information...
I am getting started for using Mvvm architecture, but I am facing problem with my adapter -> It never called
I have an adapter here :
class LocationResultAdapter: RecyclerView.Adapter<LocationResultAdapter.ViewHolder>() {
private lateinit var resultList:List<ResultSearchBean>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ItemPostBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_post, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(resultList[position])
}
override fun getItemCount(): Int {
return if (resultList.isNotEmpty()) {
resultList.size
} else {
0
}
}
fun updateList(list: List<ResultSearchBean>) {
resultList = list
notifyDataSetChanged()
}
class ViewHolder(private val binding: ItemPostBinding):RecyclerView.ViewHolder(binding.root){
private val viewModel = LocationViewModel()
fun bind(resultSearchBean: ResultSearchBean){
viewModel.bind(resultSearchBean)
binding.viewModel = viewModel
}
}
}
this adapter is called in my ViewModel
class LocationResultViewModel(private val resultSearchDao: ResultSearchDao): BaseViewModel() {
#Inject
lateinit var mFi9Api: Fi9Api
private lateinit var mSubscription: Disposable
var mSearchText: String = ""
val adapter: LocationResultAdapter = LocationResultAdapter()
override fun onCleared() {
super.onCleared()
mSubscription.dispose()
}
fun search(searchText: String) {
resultLocations(searchText)
}
private fun resultLocations(searchText: String) {
mSearchText = searchText
mSubscription = mFi9Api.getLocation(mSearchText)
.flatMap { resource ->
return#flatMap Observable.fromCallable {
resultSearchDao.deleteAll()
resultSearchDao.insertAll(resource.distinctBy { r -> r.label })
}
}
.concatMap {
return#concatMap resultSearchDao.all()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(GetLocationListSubscriber())
}
inner class GetLocationListSubscriber : ResourceObserver<List<ResultSearchBean>>() {
override fun onNext(#NonNull t: List<ResultSearchBean>) {
adapter.updateList(t)
}
override fun onError(#NonNull e: Throwable) {
}
override fun onComplete() {
// Nothing to do
}
}
In my activity I have this following code :
class LocationResultActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: LocationResultViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.resultList.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
viewModel = ViewModelProviders.of(this, ViewModelFactory(this)).get(LocationResultViewModel::class.java)
binding.viewModel = viewModel
search_btn.setOnClickListener {
viewModel.search(search_et.text.toString())
}
}
}
Only the method: adapter.updateList(t) is called.
I put some break point on onCreateViewHolder, OnBindViewHolder, getItemCount but it never called
What did I do that does not work?
Thank you for your help
I think you need to assign your adapter to your RecyclerView.
Try this:
binding.resultList.adapter = viewModel.adapter
I would like to show my items in a recycler view via RoomDatabase. But when I start application - it shows empty screen with only title.
It's an adapter for recycler view
class AlarmAdapter : ListAdapter<Alarm, AlarmAdapter.AlarmHolder>(DIFF_CALLBACK) {
private lateinit var alarmViewModel: AlarmViewModel
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Alarm>() {
override fun areItemsTheSame(oldItem: Alarm, newItem: Alarm): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Alarm, newItem: Alarm): Boolean {
return oldItem.days == newItem.days && oldItem.timeInMinutes == newItem.timeInMinutes
&& oldItem.departTimeInMinutes == newItem.departTimeInMinutes && oldItem.fromAddress == newItem.fromAddress
&& oldItem.toAddress == newItem.toAddress && oldItem.isEnabled == newItem.isEnabled
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlarmHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.alarm_item, parent, false)
alarmViewModel = ViewModelProviders.of(parent.context as FragmentActivity).get(AlarmViewModel::class.java)
return AlarmHolder(itemView)
}
override fun onBindViewHolder(holder: AlarmHolder, position: Int) {
val currentAlarm = getItem(position)
holder.fromAddress.text = currentAlarm.fromAddress
// and other texts
holder.alarmSwitch.isEnabled = currentAlarm.isEnabled
holder.alarmSwitch.setOnClickListener {
holder.alarmSwitch.toggle()
currentAlarm.isEnabled = !currentAlarm.isEnabled
alarmViewModel.update(currentAlarm)
}
}
inner class AlarmHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var fromAddress = itemView.findViewById<TextView>(R.id.alarm_item_from_address)
// and other resources
}
}
Here is a part of MainActivity
class MainActivity : AppCompatActivity() {
companion object {
private const val DAY_MINUTES = 24 * 60
}
private var currentEditAlarmDialog: EditAlarmDialog? = null
private lateinit var alarmViewModel: AlarmViewModel
private lateinit var adapter: AlarmAdapter
private lateinit var layoutManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
setContentView(R.layout.activity_main)
layoutManager = LinearLayoutManager(this)
recycler_view.layoutManager = layoutManager
recycler_view.setHasFixedSize(true)
adapter = AlarmAdapter()
recycler_view.adapter = adapter
alarmViewModel = ViewModelProviders.of(this).get(AlarmViewModel::class.java)
alarmViewModel.getAllAlarms().observe(this, Observer<List<Alarm>> {
adapter.submitList(it)
})
}
AlarmViewModel class
class AlarmViewModel(application: Application) : AndroidViewModel(application) {
private val repository = AlarmRepository(application)
private val allAlarms = repository.getAllAlarms()
fun insert(alarm: Alarm) {
repository.insert(alarm)
}
fun update(alarm: Alarm) {
repository.update(alarm)
}
fun delete(alarm: Alarm) {
repository.delete(alarm)
}
fun deleteAllAlarms() {
repository.deleteAllNotes()
}
fun getAllAlarms() = allAlarms
fun getAlarmById(id: Int) = repository.getAlarmById(id)
}
It is a repository
class AlarmRepository(application: Application) {
private var alarmDao: AlarmDao
private var allAlarms: LiveData<List<Alarm>>
private val uiScope = CoroutineScope(Dispatchers.IO)
init {
val database: AlarmDatabase = AlarmDatabase.getInstance(application.applicationContext)!!
alarmDao = database.alarmDao()
allAlarms = alarmDao.getAllAlarms()
}
fun getAllAlarms() = allAlarms
fun insert(alarm: Alarm) {
uiScope.launch { alarmDao.insert(alarm) }
}
fun delete(alarm: Alarm) {
uiScope.launch { alarmDao.delete(alarm) }
}
fun deleteAllNotes() {
uiScope.launch { alarmDao.deleteAllAlarms() }
}
fun update(alarm: Alarm) {
uiScope.launch { alarmDao.update(alarm) }
}
fun getAlarmById(id: Int) = alarmDao.getAlarmById(id)
}
And the Dao class
#Dao
interface AlarmDao {
#Insert
fun insert(alarm: Alarm)
#Delete
fun delete(alarm: Alarm)
#Update
fun update(alarm: Alarm)
#Query("DELETE FROM alarm_table")
fun deleteAllAlarms()
#Query("SELECT * FROM alarm_table ORDER BY days ASC")
fun getAllAlarms() : LiveData<List<Alarm>>
#Query("SELECT * FROM alarm_table WHERE id = :id")
fun getAlarmById(id: Int) : Alarm
}
What can I do to solve this problem?
If you need more code - just tell me about that!
In your AlarmViewModel class, put the call to the repo in the getAllAlarms method.
fun getAllAlarms() {
repository.getAllAlarms()
}
remove this from init method
allAlarms = alarmDao.getAllAlarms()
and put
fun getAllAlarms() = alarmDao.getAllAlarms()