我目前正在构建一个具有主要活动的应用程序,该应用程序托管一个navHostFragment,以及3个通过底部导航栏连接的片段。我的目标是让每个片段都使用“回收者视图”。我正在使用带有适配器和LiveData的Room数据库作为“数据”。启动应用程序时,我想转到电子钱包片段,并查看textViews的垂直列表。因为我是新手,所以刚开始,所以我只想拥有一个非常简单的仅包含文本的数据库,然后以垂直格式进行布局。没什么太疯狂了。任何帮助将不胜感激。
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)
}
}
}
}
卡片
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()
}
我可以启动应用程序,但是一旦我进入电子钱包片段,应用程序就会崩溃,并显示以下内容:
2020-04-23 19:24:18.676 5048-5048 / com.example.android.pointmax E / AndroidRuntime:FATAL EXCEPTION:main流程:com.example.android.pointmax,PID:5048java.lang.RuntimeException:无法创建com.example.android.pointmax.ui.wallet.WalletViewModel类的实例在androidx.lifecycle.ViewModelProvider $ AndroidViewModelFactory.create(ViewModelProvider.java:275)在androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)在androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)在androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)在com.example.android.pointmax.ui.wallet.WalletFragment.onCreateView(WalletFragment.kt:31)在androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)在androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)在androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)在androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)在androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)在androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)在androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)在androidx.fragment.app.FragmentManager $ 4.run(FragmentManager.java:413)在android.os.Handler.handleCallback(Handler.java:883)在android.os.Handler.dispatchMessage(Handler.java:100)在android.os.Looper.loop(Looper.java:214)在android.app.ActivityThread.main(ActivityThread.java:7356)在java.lang.reflect.Method.invoke(本机方法)在com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:492)在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)引起原因:java.lang.reflect.InvocationTargetException在java.lang.reflect.Constructor.newInstance0(本机方法)在java.lang.reflect.Constructor.newInstance(Constructor.java:343)在androidx.lifecycle.ViewModelProvider $ AndroidViewModelFactory.create(ViewModelProvider.java:267)在androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)在androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)在androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)在com.example.android.pointmax.ui.wallet.WalletFragment.onCreateView(WalletFragment.kt:31)在androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)在androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)在androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)在androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)在androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)在androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)在androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)在androidx.fragment.app.FragmentManager $ 4.run(FragmentManager.java:413)在android.os.Handler.handleCallback(Handler.java:883)在android.os.Handler.dispatchMessage(Handler.java:100)在android.os.Looper.loop(Looper.java:214)在android.app.ActivityThread.main(ActivityThread.java:7356)在java.lang.reflect.Method.invoke(本机方法)在com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:492)在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)由以下原因引起:java.lang.RuntimeException:找不到com.example.android.pointmax.database.CardRoomDatabase的实现。 CardRoomDatabase_Impl不存在在androidx.room.Room.getGeneratedImplementation(Room.java:94)在androidx.room.RoomDatabase $ Builder.build(RoomDatabase.java:952)位于com.example.android.pointmax.database.CardRoomDatabase $ Companion.getDatabase(CardRoomDatabase.kt:37)在com.example.android.pointmax.ui.wallet.WalletViewModel。(WalletViewModel.kt:20)
堆栈跟踪的最重要部分在这里
`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)`
所以问题是Room无法生成CardRoomDatabase_Impl类(抽象类CardRoomDatabase的实现)。由于您正确使用了Room注解,因此我猜出问题的唯一原因-您没有在build.gradle(应用程序级别)中包含注解处理器。检查它是否在dependencies-section中:
kapt "androidx.room:room-compiler:2.2.5"