App crashes when navigating to a fragment that uses a RecyclerView and Room database with LiveData - kotlin

I am currently building an app with a main activity which hosts a navHostFragment, and 3 fragments that are connected by a bottom navigation bar. My goal is to have each fragment use a Recycler View. I am using a Room database with an Adapter and LiveData for the "data". When I launch the app, I want to go to the Wallet fragment and see a vertical list of textViews. Because I am new and just starting out with this, I just wanted to have a very simple database of just text and then lay it out in a vertical format. Nothing too crazy yet. Any help would be greatly appreciated.
MainActivity
package com.example.android.pointmax
import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import timber.log.Timber
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Plant tree to enable Debugging with Timber
Timber.plant(Timber.DebugTree())
// Find the bottomNavigation bar
val navView: BottomNavigationView = findViewById(R.id.nav_view)
// Find the fragment that will host the different fragments
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_wallet, R.id.navigation_recommended
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
WalletFragment
package com.example.android.pointmax.ui.wallet
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.CardAdapter
import com.example.android.pointmax.R
class WalletFragment : Fragment() {
private lateinit var viewManager: RecyclerView.LayoutManager
private lateinit var viewAdapter: RecyclerView.Adapter<*>
private lateinit var recyclerView: RecyclerView
private lateinit var viewModel: WalletViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_wallet, container, false)
recyclerView = rootView.findViewById(R.id.wallet_recyclerview)
viewModel = ViewModelProvider(
this
).get(WalletViewModel::class.java)
val linearLayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
viewManager = linearLayoutManager
// Observe the ViewModel
viewModel.allCards.observe(viewLifecycleOwner, Observer { cards ->
viewAdapter = CardAdapter(cards)
})
return rootView
}
}
WalletViewModel
package com.example.android.pointmax.ui.wallet
import android.app.Application
import androidx.lifecycle.*
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CardRepository
import com.example.android.pointmax.database.CardRoomDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class WalletViewModel(application: Application) : AndroidViewModel(application) {
private val repository: CardRepository
// Using LiveData and caching what getAlphabetizedWords returns has several benefits:
// - We can put an observer on the data (instead of polling for changes) and only update the
// the UI when the data actually changes.
// - Repository is completely separated from the UI through the ViewModel.
val allCards: LiveData<List<Card>>
init {
val cardsDao = CardRoomDatabase.getDatabase(application, viewModelScope).cardDao()
repository = CardRepository(cardsDao)
allCards = repository.allCards
}
/**
* Launching a new coroutine to insert the data in a non-blocking way
*/
fun insert(card: Card) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(card)
}
}
CardAdapter
package com.example.android.pointmax
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.database.Card
class CardAdapter internal constructor(
private var cards: List<Card>
) : RecyclerView.Adapter<CardAdapter.CardViewHolder>() {
inner class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val cardItemView: TextView = itemView.findViewById(R.id.textView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item, parent, false)
return CardViewHolder(itemView)
}
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
val current = cards[position]
holder.cardItemView.text = current.toString()
}
internal fun setWords(cards: List<Card>) {
this.cards = cards
notifyDataSetChanged()
}
override fun getItemCount() = cards.size
}
CardRepository
package com.example.android.pointmax.database
import androidx.lifecycle.LiveData
// Declares the DAO as a private property in the constructor. Pass in the DAO
// instead of the whole database, because you only need access to the DAO
class CardRepository(private val cardDao: CardDao) {
// Room executes all queries on a separate thread.
// Observed LiveData will notify the observer when the data has changed.
val allCards: LiveData<List<Card>> = cardDao.getCards()
suspend fun insert(card: Card) {
cardDao.insert(card)
}
}
CardRoomDatabase
package com.example.android.pointmax.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
// Annotates class to be a Room Database with a table (entity) of the Word class
#Database(entities = arrayOf(Card::class), version = 1, exportSchema = false)
public abstract class CardRoomDatabase : RoomDatabase() {
abstract fun cardDao(): CardDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
private var INSTANCE: CardRoomDatabase? = null
fun getDatabase(
context: Context,
scope: CoroutineScope
): CardRoomDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
CardRoomDatabase::class.java,
"card_database"
).addCallback(CardDatabaseCallback(scope)).build()
INSTANCE = instance
return instance
}
}
private class CardDatabaseCallback(
private val scope: CoroutineScope
) : RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
INSTANCE?.let { database ->
scope.launch {
populateDatabase(database.cardDao())
}
}
}
suspend fun populateDatabase(cardDao: CardDao) {
// Delete all content here.
cardDao.deleteAll()
// Add sample words.
var card = Card("Petal Credit Card")
cardDao.insert(card)
card = Card("Discover IT")
cardDao.insert(card)
}
}
}
}
Card
package com.example.android.pointmax.database
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
#Entity(tableName = "card_table")
data class Card(
#PrimaryKey
#ColumnInfo(name = "cardName")
var card: String
)
CardDao
package com.example.android.pointmax.database
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
#Dao
interface CardDao {
#Query("SELECT * from card_table")
fun getCards(): LiveData<List<Card>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(card: Card)
#Query("DELETE FROM card_table")
suspend fun deleteAll()
}
I can launch the application but as soon as I go to the Wallet fragment, the application crashes with the following:
2020-04-23 19:24:18.676 5048-5048/com.example.android.pointmax E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.pointmax, PID: 5048
java.lang.RuntimeException: Cannot create an instance of class com.example.android.pointmax.ui.wallet.WalletViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:275)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.example.android.pointmax.ui.wallet.WalletFragment.onCreateView(WalletFragment.kt:31)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:267)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150) 
at com.example.android.pointmax.ui.wallet.WalletFragment.onCreateView(WalletFragment.kt:31) 
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698) 
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320) 
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187) 
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224) 
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997) 
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953) 
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849) 
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413) 
at android.os.Handler.handleCallback(Handler.java:883) 
at android.os.Handler.dispatchMessage(Handler.java:100) 
at android.os.Looper.loop(Looper.java:214) 
at android.app.ActivityThread.main(ActivityThread.java:7356) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 
Caused by: java.lang.RuntimeException: cannot find implementation for com.example.android.pointmax.database.CardRoomDatabase. CardRoomDatabase_Impl does not exist
at androidx.room.Room.getGeneratedImplementation(Room.java:94)
at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:952)
at com.example.android.pointmax.database.CardRoomDatabase$Companion.getDatabase(CardRoomDatabase.kt:37)
at com.example.android.pointmax.ui.wallet.WalletViewModel.(WalletViewModel.kt:20)

The most essential part of your stack trace is here
`Caused by: java.lang.RuntimeException: cannot find implementation for com.example.android.pointmax.database.CardRoomDatabase. CardRoomDatabase_Impl does not exist at androidx.room.Room.getGeneratedImplementation(Room.java:94) at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:952) at com.example.android.pointmax.database.CardRoomDatabase$Companion.getDatabase(CardRoomDatabase.kt:37) at com.example.android.pointmax.ui.wallet.WalletViewModel.(WalletViewModel.kt:20)`
So the problem is Room couldn't generate class CardRoomDatabase_Impl (implementation of your abstract class CardRoomDatabase). Since you use Room annotation correctly, the only reason of your problem I could guess - you haven't included annotation processor in your build.gradle (app level). Check if it is in dependencies-section:
kapt "androidx.room:room-compiler:2.2.5"

Related

adding onclicklistener to recycler view with viewbinding, kotlin

Trying to add an onclicklistener to the items in my recycler view, that will use an intent to open another activity. I've tried finding examples, but I can only find examples using Java or Kotlin examples that aren't using viewbinding.
package com.truuce.anotherrvtest
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import com.truuce.anotherrvtest.databinding.ActivityHeroBinding
class HeroActivity : AppCompatActivity() {
var binding: ActivityHeroBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHeroBinding.inflate(layoutInflater)
setContentView(binding?.root)
val adapter = CardAdapter(HeroList.heroList)
binding?.heroRV?.adapter = adapter
binding?.heroRV?.layoutManager = GridLayoutManager(applicationContext, 3)
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
}
package com.truuce.anotherrvtest
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.truuce.anotherrvtest.databinding.HeroCardBinding
class CardAdapter(val heroList: List<Hero>) : RecyclerView.Adapter<CardAdapter.MainViewHolder>() {
inner class MainViewHolder(val heroBinding: HeroCardBinding) :
RecyclerView.ViewHolder(heroBinding.root) {
fun bindHero(hero: Hero){
heroBinding.heroNameTV.text = hero.heroName
heroBinding.heroIV.setImageResource(hero.image)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
return MainViewHolder(HeroCardBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val hero = heroList[position]
holder.bindHero(hero)
}
override fun getItemCount() = heroList.size
}
tried adding View.OnClickListener to MainViewHolder, then implemented a member. OnClick(p0: View){}, but no idea how to get it working.
You should add a functional property for a click listener in your adapter.
The Activity can set the item click listener behavior.
class CardAdapter(
val heroList: List<Hero>,
val itemClickListener: (Hero)->Unit
) : RecyclerView.Adapter<CardAdapter.MainViewHolder>() {
inner class MainViewHolder(val heroBinding: HeroCardBinding) :
RecyclerView.ViewHolder(heroBinding.root) {
fun bindHero(hero: Hero) = with(heroBinding) {
heroNameTV.text = hero.heroName
heroIV.setImageResource(hero.image)
root.setOnClickListener { itemClickListener(hero) }
}
}
//...
}
// In Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHeroBinding.inflate(layoutInflater)
setContentView(binding?.root)
val adapter = CardAdapter(HeroList.heroList) { hero ->
// do something with hero item when it's clicked
}
binding?.heroRV?.adapter = adapter
binding?.heroRV?.layoutManager = GridLayoutManager(applicationContext, 3)
}

Jetpack compose - trying to get sound to play in viewmodel fun

I'm trying to play a sound outside of #Copmosable because of my application structure.
I have a validation routine which is in my viewmodel and based on the result I would like to trigger a sound but I cannot seem to get context working outside of #Composable
I get the following error in the MasterViewModel:
None of the following functions can be called with the arguments supplied.
create(Context!, Uri!) defined in android.media.MediaPlayer
create(Context!, Int) defined in android.media.MediaPlayer
Any pointers would be great thanks!!
package com.example.soundtest
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.example.soundtest.ui.theme.SoundTestTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SoundTestTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
Greeting()
}
}
}
}
}
#Composable
fun Greeting(mastVM: MasterViewModel = MasterViewModel()) {
Text("Play a sound...")
mastVM.playSound()
}
and the MasterViewModel
package com.example.soundtest
import android.media.MediaPlayer
class MasterViewModel {
fun playSound() {
val mp: MediaPlayer = MediaPlayer.create(this, R.raw.correct)
}
}
I have correct.mp3 save under res->raw-correct.mp3
MasterViewModel Error
I got it working!!!
I just include
#Composable
fun Greeting(mastVM: MasterViewModel = MasterViewModel()) {
val context = LocalContext.current
Text("Play a sound...")
mastVM.playSound(context: context)
}
and then in the MasterViewModel like this
fun playSound(context: Context) {
val mp: MediaPlayer = MediaPlayer.create(context, R.raw.correct)
}

App crashes when initializing view model (in fragment) that gets items from room database

I am currently building an app with a main activity that hosts a navHostFragment, and 2 fragments connected by a bottom navigation bar. One of the fragments uses a recyclerView that is meant to display items that display data from a room database. I am using the room database with an adapter and LiveData for the items. When I launch the app and go to this fragment it instantly crashes and after testing around I found out that it crashes when I try to use the viewModel in the code in any fragment and from the crash report I know the problem is in the application I use to hold the database. I am quite new at this so I don't know what the error is or how to fix it. Any help would be greatly apreciated.
TaskHomeFragment(Example fragment that initializes viewModel)
package com.example.untitledcalendarapp.overview.home
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.untitledcalendarapp.databinding.FragmentTaskHomeBinding
import com.example.untitledcalendarapp.overview.CalendarApplication
import com.example.untitledcalendarapp.overview.TaskListAdapter
class TaskHomeFragment : Fragment() {
private val viewModel: HomeViewModel by activityViewModels{
HomeViewModelFactory(
(activity?.application as CalendarApplication).database.taskDao()
)
}
private var _binding: FragmentTaskHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentTaskHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Create and bind Adapter
val adapter = TaskListAdapter{ }
binding.taskHomeGrid.adapter = adapter
//observe items
viewModel.allItems.observe(this.viewLifecycleOwner){ items ->
items.let{
adapter.submitList(it)
}
}
//set layout manager
binding.taskHomeGrid.layoutManager = LinearLayoutManager(this.context)
}
}
ViewModel
package com.example.untitledcalendarapp.overview.home
import androidx.lifecycle.*
import com.example.untitledcalendarapp.data.Task
import com.example.untitledcalendarapp.data.TaskDao
class HomeViewModel(private val taskDao: TaskDao): ViewModel() {
//Get items from database
val allItems: LiveData<List<Task>> = taskDao.getItems().asLiveData()
}
class HomeViewModelFactory(private val taskDao: TaskDao) : ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(HomeViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return HomeViewModel(taskDao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
Task (entity)
package com.example.untitledcalendarapp.data
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.util.*
#Entity
data class Task(
#PrimaryKey(autoGenerate = true)
val id:Int = 0,
#ColumnInfo(name = "title")
val taskTitle: String,
#ColumnInfo(name = "desription")
val taskDescription: String,
#ColumnInfo(name = " date")
val taskDate: Date?,
#ColumnInfo(name = "priority")
val taskPrio: Int
)
TaskConverters (Type converters)
package com.example.untitledcalendarapp.data
import androidx.room.TypeConverter
import java.util.*
class TaskConverters {
#TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
#TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time?.toLong()
}
}
TaskDao
package com.example.untitledcalendarapp.data
import androidx.room.*
import kotlinx.coroutines.flow.Flow
#Dao
interface TaskDao {
#Query("SELECT * from task ORDER BY title ASC")
fun getItems(): Flow<List<Task>>
#Query("SELECT * from task WHERE id = :id")
fun getItem(id: Int): Flow<Task>
#Query("SELECT * from task ORDER BY priority DESC")
fun getItemsByPrio(): Flow<List<Task>>
// Specify the conflict strategy as IGNORE, when the user tries to add an
// existing Item into the database.
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(task: Task)
#Update
suspend fun update(task: Task)
#Delete
suspend fun delete(task: Task)
}
TaskRoomDatabase (Database class)
package com.example.untitledcalendarapp.data
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
/**
* Database class with a singleton INSTANCE object.
*/
#Database(entities = [Task::class], version = 1, exportSchema = false)
#TypeConverters(TaskConverters::class)
abstract class TaskRoomDatabase : RoomDatabase() {
abstract fun taskDao(): TaskDao
companion object {
#Volatile
private var INSTANCE:TaskRoomDatabase? = null
fun getDatabase(context: Context): TaskRoomDatabase {
// 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,
TaskRoomDatabase::class.java,
"task_database"
)
// Wipes and rebuilds instead of migrating if no Migration object.
// Migration is not part of this codelab.
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
// return instance
instance
}
}
}
}
CalendarApplication (Database Application)
package com.example.untitledcalendarapp.overview
import android.app.Application
import com.example.untitledcalendarapp.data.TaskRoomDatabase
class CalendarApplication : Application(){
val database: TaskRoomDatabase by lazy { TaskRoomDatabase.getDatabase(this) }
}
this is the crash report I get
2021-08-10 20:36:28.789 8076-8076/com.example.untitledcalendarapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.untitledcalendarapp, PID: 8076
java.lang.ClassCastException: android.app.Application cannot be cast to com.example.untitledcalendarapp.overview.CalendarApplication
at com.example.untitledcalendarapp.overview.home.TaskHomeFragment$viewModel$2.invoke(TaskHomeFragment.kt:17)
at com.example.untitledcalendarapp.overview.home.TaskHomeFragment$viewModel$2.invoke(TaskHomeFragment.kt:14)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:52)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41)
at com.example.untitledcalendarapp.overview.home.TaskHomeFragment.getViewModel(Unknown Source:2)
at com.example.untitledcalendarapp.overview.home.TaskHomeFragment.onViewCreated(TaskHomeFragment.kt:39)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2987)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:546)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1971)
at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:305)
at androidx.viewpager2.adapter.FragmentStateAdapter.placeFragmentInViewHolder(FragmentStateAdapter.java:341)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:276)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:67)
at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:7556)
at androidx.recyclerview.widget.RecyclerView$5.addView(RecyclerView.java:860)
at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:107)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:8601)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8559)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8547)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1641)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1391)
at androidx.recyclerview.widget.LinearLayoutManager.scrollHorizontallyBy(LinearLayoutManager.java:1116)
at androidx.recyclerview.widget.RecyclerView.scrollStep(RecyclerView.java:1838)
at androidx.recyclerview.widget.RecyclerView.scrollByInternal(RecyclerView.java:1940)
at androidx.recyclerview.widget.RecyclerView.onTouchEvent(RecyclerView.java:3391)
at androidx.viewpager2.widget.ViewPager2$RecyclerViewImpl.onTouchEvent(ViewPager2.java:991)
at android.view.View.dispatchTouchEvent(View.java:13415)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3054)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2741)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
2021-08-10 20:36:28.790 8076-8076/com.example.untitledcalendarapp E/AndroidRuntime: at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:465)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1849)
at android.app.Activity.dispatchTouchEvent(Activity.java:3993)
at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
at com.android.tools.profiler.support.event.WindowProfilerCallback.dispatchTouchEvent(WindowProfilerCallback.java:61)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:423)
at android.view.View.dispatchPointerEvent(View.java:13674)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5482)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5285)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4788)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4841)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4807)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4947)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4815)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5004)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4788)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4841)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4807)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4815)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4788)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7505)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7474)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7435)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7630)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:188)
at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:178)
at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7581)
at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7654)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
at android.view.Choreographer.doCallbacks(Choreographer.java:790)
at android.view.Choreographer.doFrame(Choreographer.java:718)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
2021-08-10 20:36:28.806 8076-8076/com.example.untitledcalendarapp I/Process: Sending signal. PID: 8076 SIG: 9

Kotlin Recycleview ,how to make onItemClick for views

I am attempting to make a function :
In recyclerView
when you click the user image ,navigate to the userActivity,
and when you click the "gift" icon ,navigate to otherActivity .
As follow is my Adapter.kt :
package Users.UserReceiveGiftItem
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gearsrun.www.R
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.user_receive_gift_item.view.*
class UserReceiveGiftAdapter(val userList : List<UserReceiveGiftItem>) : RecyclerView.Adapter<UserReceiveGiftAdapter.UserHolder>(){
private lateinit var mListener :onItemClickListener
interface onItemClickListener{
fun onItemClick(view:View,position: Int)
}
fun setOnItemClickListener(listener: onItemClickListener){
mListener = listener
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): UserHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return UserHolder(layoutInflater.inflate(R.layout.user_receive_gift_item,parent,false),mListener)
}
override fun onBindViewHolder(holder: UserReceiveGiftAdapter.UserHolder, position: Int) {
holder.render(userList[position])
}
override fun getItemCount(): Int = userList.size
class UserHolder(val view : View,listener:onItemClickListener) : RecyclerView.ViewHolder(view){
fun render(userList: UserReceiveGiftItem){
Picasso.get().load(userList.user_img).into(view.user_img)
view.user_name.text = userList.user_name
view.time.text = userList.time
view.userId.text = userList.userId
view.giftImg.setImageResource(userList.giftImg)
}
init {
view.setOnClickListener {
listener.onItemClick(it,absoluteAdapterPosition)
}
}
}
}
And I use in the Activity :
package com.gearsrun.www.UI.Receive
import Users.UserReceiveGiftItem.UserReceiveGiftAdapter
import Users.UserReceiveGiftItem.UserReceiveGiftItem
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.ActionBar
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.gearsrun.www.R
import com.gearsrun.www.UI.Gift.AwaitingToUnwrapActivity
import com.gearsrun.www.UI.Sunflower.SunflowerAvailableActivity
import com.gearsrun.www.UI.User.UserDetailActivity
import kotlinx.android.synthetic.main.activity_who_receive_gift.*
import kotlinx.android.synthetic.main.user_receive_gift_item.view.*
class ReceiveActivity : AppCompatActivity() {
val userList : List<UserReceiveGiftItem> = listOf(
UserReceiveGiftItem(
"https://i.pinimg.com/564x/63/85/68/63856877880614e0dab080071513156f.jpg",
"Sharry",
"10 mins ago",
"517ddY",
R.drawable.donut
),
)
lateinit var gift_unwrap : ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_receive)
changeColor(R.color.font_green)
initRecycler()
gift_unwrap = findViewById(R.id.gift_unwrap)
gift_unwrap.setOnClickListener {
}
}
private fun changeColor(resourseColor: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor = ContextCompat.getColor(applicationContext, resourseColor)
}
val bar: ActionBar? = supportActionBar
if (bar != null) {
bar.setBackgroundDrawable(ColorDrawable(resources.getColor(resourseColor)))
}
}
fun initRecycler(){
rvUser.layoutManager = LinearLayoutManager(this)
val adapter = UserReceiveGiftAdapter(userList)
rvUser.adapter = adapter
adapter.setOnItemClickListener(object:UserReceiveGiftAdapter.onItemClickListener{
override fun onItemClick(view: View, position: Int) {
view.user_img.setOnClickListener {
val intent = Intent(this#ReceiveActivity,UserDetailActivity::class.java)
startActivity(intent)
}
view.giftImg.setOnClickListener {
val intent =Intent(this#ReceiveActivity,SunflowerAvailableActivity::class.java)
startActivity(intent)
}
}
})
}
}
It is a bit rare that it is able to navigate successfully ,however ,it dosen't react for the first click ..Could you please take a look my code ?Thank you guys in advance !!
You are using the click listener of the list item to add more click listeners to its children. So the first time you click it, the children don't have listeners yet and won't do anything. It is also error prone that a parent and its children both have click listeners, because it's undefined what will happen if you change the click listener as you are clicking it.
Instead, you should define your interface in the adapter to handle both types of clicks. In the ViewHolder, set click listeners only on the relevant children to be clicked.
Also, I think you're misusing lateinit. I would make the property nullable. And it is redundant to have a setter function for a property's value.
If you make the ViewHolder class inner, then you don't have to pass instances of the listener to the view holder instances, which will cause problems if you ever change the Adapter's listener.
Finally, personally I think the listener should return the list item, not the specific view and position in the list. Those are implementation details that the outer class doesn't need to know about.
class UserReceiveGiftAdapter(val userList : List<UserReceiveGiftItem>) : RecyclerView.Adapter<UserReceiveGiftAdapter.UserHolder>(){
var onItemClickListener: OnItemClickListener? = null
interface OnItemClickListener{
fun onUserClick(item: UserReceiveGiftItem)
fun onGiftClick(item: UserReceiveGiftItem)
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): UserHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return UserHolder(layoutInflater.inflate(R.layout.user_receive_gift_item,parent,false))
}
override fun onBindViewHolder(holder: UserReceiveGiftAdapter.UserHolder, position: Int) {
holder.render(userList[position])
}
override fun getItemCount(): Int = userList.size
inner class UserHolder(val view : View) : RecyclerView.ViewHolder(view){
fun render(userList: UserReceiveGiftItem){
Picasso.get().load(userList.user_img).into(view.user_img)
view.user_name.text = userList.user_name
view.time.text = userList.time
view.userId.text = userList.userId
view.giftImg.setImageResource(userList.giftImg)
}
init {
view.user_img.setOnClickListener {
onItemClickListener?.onUserClick(userList[absoluteAdapterPosition])
}
view.giftImg.setOnClickListener {
onItemClickListener?.onGiftClick(userList[absoluteAdapterPosition])
}
}
}
}
In Activity or Fragment:
adapter.onItemClickListener = object: UserReceiveGiftAdapter.OnItemClickListener {
override fun onUserClick(item: UserReceiveGiftItem) {
val intent = Intent(this#ReceiveActivity, UserDetailActivity::class.java)
startActivity(intent)
}
override fun onGiftClick(item: UserReceiveGiftItem) {
val intent = Intent(this#ReceiveActivity, SunflowerAvailableActivity::class.java)
startActivity(intent)
}
}
What I think after looking your code is, that you are setting adapter to your recyclerview first and then you are injecting a click listener to adapter.
What happens here is that some items from recycler view are loaded and they wouldn't have the click listener at that time,
for example if you scroll down, the newly appeared items on the screen will have proper click listeners, now if you scroll up, the previous one's will also have listener attached.
What to do to get rid of this problem:
Change your initRecycler() method as below:
fun initRecycler(){
rvUser.layoutManager = LinearLayoutManager(this)
val adapter = UserReceiveGiftAdapter(userList)
adapter.setOnItemClickListener(object:UserReceiveGiftAdapter.onItemClickListener{
override fun onItemClick(view: View, position: Int) {
view.user_img.setOnClickListener {
val intent = Intent(this#ReceiveActivity,UserDetailActivity::class.java)
startActivity(intent)
}
view.giftImg.setOnClickListener {
val intent =Intent(this#ReceiveActivity,SunflowerAvailableActivity::class.java)
startActivity(intent)
}
}
})
//set adapter after setting click listener to your adapter.
rvUser.adapter = adapter
}

The app Crashes when i add -notifyDataSetChanged()- the code run perfect and get the posts in the database but

this code is inside a fragment I have a recycler view and the array list all set
the code runs perfect and get the posts in the database and get the Log messages i have entered until I add this line
postAdapter!!.notifyDataSetChanged()
which is line 81 on my code I have Bolded it so you know what I talk about
am new to coding so any small help will help me
package com.example.foonection.Fragments
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.foonection.AddNewPostActivity
import com.example.foonection.Post
import com.example.foonection.PostAdapter
import com.example.foonection.R
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import kotlinx.android.synthetic.main.fragment_feed_seller.view.*
import java.util.ArrayList
/**
* A simple [Fragment] subclass.
*/
class FeedSellerFragment : Fragment() {
private var postAdapter: PostAdapter? = null
private var postList: MutableList<Post>? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_feed_seller, container, false)
var recyclerView: RecyclerView? = null
recyclerView = view.findViewById(R.id.timeline_view_feed)
val linearLayoutManger = LinearLayoutManager(context)
linearLayoutManger.reverseLayout = true
linearLayoutManger.stackFromEnd = true
recyclerView.layoutManager = linearLayoutManger
postList = ArrayList()
postAdapter = context?.let { PostAdapter(it,postList as ArrayList<Post>) }
recyclerView.adapter = postAdapter
retrievePosts()
view.add_post_feed_btn.setOnClickListener {
Log.d("Log","Add Post btn Clicked")
val intent = Intent(context, AddNewPostActivity::class.java)
startActivity(intent)
}
return view
}
private fun retrievePosts(){
Log.d("Log","Trying To Retrieve Posts")
val postsRef = FirebaseDatabase.getInstance().reference.child("Posts")
postsRef.addValueEventListener(object : ValueEventListener{
override fun onDataChange(p0: DataSnapshot) {
postList?.clear()
if (p0.exists())
{
Log.d("Log","p0 is existed")
for (snapshot in p0.children)
{
Log.d("Log","for loop snapshot")
val post = snapshot.getValue(Post :: class.java)
postList!!.add(post!!)
**postAdapter!!.notifyDataSetChanged()**
}
}
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
}
call notifyDataSetChanged like this
postAdapter?.let{
it.notifyDataSetChanged()
}