最初创建应用程序时,它使用cardName并将其托管在RecyclerView中,该Viewer按名称保存了每个卡的列表。由于我还不熟悉开发,所以这样做是为了看看是否可以让RecyclerView仅使用名称。现在,我希望能够以一对多的关系向每个卡添加类别,并且按照https://developer.android.com/training/data-storage/room/relationships下的说明进行操作,但是由于我遇到以下错误,因此我可能遗漏了某些内容。理想情况下,该应用程序应与以前一样工作,仅显示名称,但现在还具有其他值,随着时间的推移,我可以将这些值慢慢添加到片段中。我非常感谢您的协助,因为我可能忽略了某些事项。预先感谢。
卡
package com.example.android.pointmax.database
import androidx.room.*
@Entity
data class Card(
@PrimaryKey(autoGenerate = true)
var cardId: Long = 0L,
var cardName: String
)
@Entity
data class Category(
@PrimaryKey(autoGenerate = true)
val categoryId: Long = 0L,
val cardCategoryId: Long = 0L,
var type: String = "General",
var earnRate: Double = 1.0,
var protection: Int = 0,
var redeemValue: String = "Cash"
)
data class CreditCards(
@Embedded val card: Card,
@Relation(
parentColumn = "cardId",
entityColumn = "cardCategoryId"
)
val categories: List<Category>
)
CardDao
package com.example.android.pointmax.database
import androidx.lifecycle.LiveData
import androidx.room.*
@Dao
interface CardDao {
@Transaction
@Query("SELECT * from Card")
fun getCards(): LiveData<List<CreditCards>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(card: Card)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCategory(category: Category)
@Query("DELETE FROM Card")
suspend fun deleteAll()
@Query("DELETE FROM Card WHERE cardName = :name")
suspend fun deleteByName(name: String)
@Query("UPDATE Card SET cardName = :newName WHERE cardName = :oldName")
suspend fun editName(newName: String, oldName: String)
}
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<CreditCards>> = cardDao.getCards()
suspend fun insert(card: Card) {
cardDao.insert(card)
}
suspend fun insertCategory(category: Category) {
cardDao.insertCategory(category)
}
suspend fun deleteByName(name: String){
cardDao.deleteByName(name)
}
suspend fun editName(newName: String, oldName: String){
cardDao.editName(newName, oldName)
}
}
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 Card 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 cards.
var card = Card(cardName = "Petal Credit Card")
cardDao.insert(card)
var category = Category(cardCategoryId = card.cardId, type = "General", earnRate = 1.5, protection = 0, redeemValue = "cash")
cardDao.insertCategory(category)
// card = Card(cardName = "Discover IT")
// cardDao.insert(card)
}
}
}
}
构建
执行任务:项目C:\ Users \ jjrog \ AndroidStudioProjects \ PointMax中的[:app:assembleDebug]
任务:app:preBuild截止日期 任务:app:preDebugBuild UP-TO-DATE 任务:app:compileDebugAidl NO-SOURCE 任务:app:generateDebugBuildConfig更新日期 任务:app:writeDebugApplicationId更新日期 任务:app:compileDebugRenderscript NO-SOURCE 任务:app:generateSafeArgsDebug截止日期 任务:app:prepareLintJar截止日期 任务:app:prepareLintJarForPublish截止日期 任务:app:generateDebugSources更新日期 任务:app:dataBindingExportBuildInfoDebug截止日期 任务:app:dataBindingMergeDependencyArtifactsDebug截止日期 任务:app:dataBindingMergeGenClassesDebug截止日期 任务:app:generateDebugResValues截止日期 任务:app:generateDebugResources最新 任务:app:mergeDebugResources最新 任务:app:dataBindingGenBaseClassesDebug截止日期 任务:app:dataBindingExportFeaturePackageIdsDebug截止日期 任务:app:mainApkListPersistenceDebug截止日期 任务:app:createDebugCompatibleScreenManifests截止日期 任务:app:extractDeepLinksDebug截止日期 任务:app:processDebugManifest截止日期 任务:app:processDebugResources截止日期 任务:app:kaptGenerateStubsDebugKotlin 任务:app:mergeDebugShaders截止日期 任务:app:compileDebugShaders截止日期 任务:app:generateDebugAssets更新日期 任务:app:mergeDebugAssets截止日期 任务:app:processDebugJavaRes NO-SOURCE 任务:app:checkDebugDuplicateClasses最新 任务:app:desugarDebugFileDependencies截止日期 任务:app:mergeExtDexDebug截止日期 任务:app:mergeDebugJniLibFolders截止日期 任务:app:mergeDebugNativeLibs截止日期 任务:app:stripDebugDebugSymbols截止日期 任务:app:validateSigningDebug截止日期
任务:app:kaptDebugKotlin 用于代码生成的ANTLR工具版本4.5.3与当前运行时版本4.7.1不匹配用于分析器编译的ANTLR运行时版本4.5.3与当前运行时版本4.7.1不匹配用于代码生成的ANTLR工具版本4.5.3与匹配用于解析器编译的当前运行时版本4.7.1ANTLR运行时版本4.5.3与当前运行时版本4.7.1C不匹配:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CreditCards.java:12:错误:查询存在问题:[SQLITE_ERROR] SQL错误或缺少数据库(没有这样的表:Category) 私有最终java.util.List类别= null; ^ C:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CardDao.java:17:错误:参数类型必须是一个类用@Entity或其集合/数组注释。 com.example.android.pointmax.database.CreditCards卡,@ org.jetbrains.annotations.NotNull()
任务:app:kaptDebugKotlin失败 ^ 失败:构建失败,并出现异常。
*出了什么问题: 任务':app:kaptDebugKotlin'的执行失败。
执行org.jetbrains.kotlin.gradle.internal.KaptExecution时发生故障 java.lang.reflect.InvocationTargetException(无错误消息)
11秒内失败 29个可执行的任务:2个已执行,27个是最新的
编辑: BindingAdapter
package com.example.android.pointmax
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CreditCards
/**
* When there are no Cards (data is null), hide the [RecyclerView], otherwise show it.
*/
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: List<CreditCards>?) {
val adapter = recyclerView.adapter as CardAdapter
adapter.submitList(data)
}
CardAdapter
package com.example.android.pointmax
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CreditCards
import com.example.android.pointmax.databinding.RecyclerviewItemBinding
import com.example.android.pointmax.ui.wallet.WalletViewModel
import kotlinx.android.synthetic.main.recyclerview_item.view.*
class CardAdapter(val onClickListener: OnClickListener) :
ListAdapter<CreditCards, CardAdapter.CardViewHolder>(DiffCallback) {
/**
* The CardViewHolder constructor takes the binding variable from the associated
* LayoutViewItem, which nicely gives it access to the full [Card] information.
*/
class CardViewHolder (private var binding: RecyclerviewItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind (cards: CreditCards){
binding.creditCard = cards
// This is important, because it forces the data binding to execute immediately,
// which allows the RecyclerView to make the correct view size measurements
binding.executePendingBindings()
}
}
/**
* Allows the RecyclerView to determine which items have changed when the [List] of [Card]
* has been updated.
*/
companion object DiffCallback : DiffUtil.ItemCallback<CreditCards>() {
override fun areItemsTheSame(oldItem: CreditCards, newItem: CreditCards): Boolean {
return oldItem.card === newItem.card
}
override fun areContentsTheSame(oldItem: CreditCards, newItem: CreditCards): Boolean {
return oldItem.card.cardName == newItem.card.cardName
}
}
/**
* Create new [RecyclerView] item views (invoked by the layout manager)
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
return CardViewHolder(RecyclerviewItemBinding.inflate(LayoutInflater.from(parent.context)))
}
/**
* Replaces the contents of a view (invoked by the layout manager)
*/
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
val current = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(current)
}
holder.bind(current)
}
/**
* Custom listener that handles clicks on [RecyclerView] items. Passes the [CreditCards]
* associated with the current item to the [onClick] function.
* @param clickListener lambda that will be called with the current [Card]
*/
class OnClickListener(val clickListener: (cards : CreditCards) -> Unit) {
fun onClick(cards:CreditCards) = clickListener(cards)
}
}
Recyclerview_item
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="creditCard"
type="com.example.android.pointmax.database.CreditCards" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/card_name"
style="@style/card_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{creditCard.card.cardName.toString()}"
android:background="@android:color/holo_orange_light"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="@dimen/activity_vertical_margin"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</layout>
fragment_wallet.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.android.pointmax.ui.wallet.WalletViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.pointmax.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/wallet_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/recyclerview_item"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:listData="@{viewModel.allCards}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
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 com.example.android.pointmax.database.CreditCards
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
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.
// The external LiveData interface to the property is immutable, so only this class can modify
val allCards: LiveData<List<CreditCards>>
// Internally, we use a MutableLiveData to handle navigation to the selected cxa
private val _navigateToSelectedCard = MutableLiveData<Card>()
// The external immutable LiveData for the navigation property
val navigateToSelectedCard: LiveData<Card>
get() = _navigateToSelectedCard
// Create a Coroutine scope using a job to be able to cancel when needed
private var viewModelJob = Job()
// the Coroutine runs using the Main (UI) dispatcher
private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)
/**
* When the card is clicked, set the [_navigateToSelectedCard] [MutableLiveData]
* @param card The [Card] that was clicked on.
*/
fun displayCardDetails(card: Card) {
_navigateToSelectedCard.value = card
}
/**
* After the navigation has taken place, make sure navigateToSelectedProperty is set to null
*/
fun displayCardDetailsComplete() {
_navigateToSelectedCard.value = null
}
init {
val cardsDao = CardRoomDatabase.getDatabase(application, viewModelScope).cardDao()
repository = CardRepository(cardsDao)
allCards = repository.allCards
}
}
关于应用中的一对多关系,我仍然面临错误:
任务:app:kaptDebugKotlin 用于代码生成的ANTLR工具版本4.5.3与当前运行时版本4.7.1不匹配用于分析器编译的ANTLR运行时版本4.5.3与当前运行时版本4.7.1不匹配用于代码生成的ANTLR工具版本4.5.3与匹配用于解析器编译的当前运行时版本4.7.1ANTLR运行时版本4.5.3与当前运行时版本4.7.1C不匹配:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CreditCards.java:12:错误:查询存在问题:[SQLITE_ERROR] SQL错误或缺少数据库(没有这样的表:Category) 私有最终java.util.List类别= null; ^ C:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CardDao.java:22:错误:com.example.android.pointmax。 database.CardDao是com.example.android.pointmax.database.CardRoomDatabase的一部分,但该实体不在数据库中。也许您忘了将com.example.android.pointmax.database.Category添加到@Database的实体部分? 公共抽象java.lang.Object insertCategory(@ org.jetbrains.annotations.NotNull() [WARN]请求了增量注释处理,但是由于以下处理器不是增量处理器,因此不支持该功能:androidx.room.RoomProcessor(DYNAMIC)。
任务:app:kaptDebugKotlin失败 ^ 失败:构建失败,并出现异常。
执行org.jetbrains.kotlin.gradle.internal.KaptExecution时发生故障 java.lang.reflect.InvocationTargetException(无错误消息)
堆栈跟踪:
由以下原因引起:org.gradle.workers.internal.DefaultWorkerExecutor $ WorkExecutionException:执行org.jetbrains.kotlin.gradle.internal.KaptExecution时发生故障
由以下原因引起:java.lang.reflect.InvocationTargetException
起因:org.jetbrains.kotlin.kapt3.base.util.KaptBaseError:注释处理时出错
答案 0 :(得分:1)
我认为,这里的问题出在您的DAO中:
formset = DetailMatiereDetailServiceFormSet(form_kwargs={'annee': annee})
NameError: name 'annee' is not defined
信用卡-只是数据类,不是Room的实体。没有与之关联的SQLite表。因此,您不能使用它来插入。假设这个类-对于Room是“只读的”(在查询过程中它仅在运行时填充了数据),并且只能将其用作查询中结果的类型。
您还有另外两个实体- Card 和 Category ,它们每个都与SQLite表关联。您应该为它们实现@Insert方法。这些表(在其中一个或两个表中)发生更改后,您将通过查询获得新的结果,从而得到 CreditCards 值。