简体   繁体   English

使用 Kotlin 在房间中进行一对多

[英]One-to-many in Room with Kotlin

The task is to open an activity with notes attached to this diary when you select a single diary.任务是在您选择单个日记时打开带有附加到此日记的注释的活动。 (one-to-many) This is how entities in the database look like: (一对多)这是数据库中实体的样子:

@Entity(tableName = "word_table")
data class Word(@ColumnInfo(name = "word") val word: String,
                @ColumnInfo(name = "description") val description : String
)
{
    @ColumnInfo(name = "id")
    @PrimaryKey(autoGenerate = true)
    var id : Long = 0

}

@Entity(tableName = "note_table")
data class Note(@ColumnInfo(name = "note_name") val note : String,
                @ColumnInfo(name = "text") val text : String,
                @ColumnInfo(name = "diaryId") val diaryId : Long
){
    @PrimaryKey(autoGenerate = true)
    var idNote : Long = 0
    }

Using a data class in NoteRepository.kt使用 NoteRepository.kt 中的数据类

data class NotesAndWords (@Embedded val word : Word,
                          @Relation(parentColumn = "id", entityColumn = "diaryId")
                          val notes : List<Note>)

And a Query in WordDao.kt以及 WordDao.kt 中的查询

@Transaction
@Query("SELECT * from word_table ")
fun getSomeNotes() : LiveData<List<NotesAndWords>>

I get the data and save it in the NoteRepository class:我获取数据并将其保存在 NoteRepository 类中:

class NoteRepository (private val wordDao : WordDao) {

    var allNotes : LiveData<List<NotesAndWords>> = wordDao.getSomeNotes()

    suspend fun insertNote(note : Note)
    {
        wordDao.insertNote(note)
    }
}

Then via NoteViewModel.kt passing data to NoteActivity.kt:然后通过 NoteViewModel.kt 将数据传递给 NoteActivity.kt:

class NoteViewModel(application: Application) : AndroidViewModel(application) {

    private val repository: NoteRepository

    val allNotes: LiveData<List<NotesAndWords>>

    init {

        val wordsDao = WordRoomDatabase.getDatabase(application, viewModelScope).wordDao()

        repository = NoteRepository(wordsDao)
        allNotes = repository.allNotes

    }

    fun insertNote(note: Note) = viewModelScope.launch {
        repository.insertNote(note)
    }
}

(NoteActivity.kt) (NoteActivity.kt)

class NoteActivity : AppCompatActivity() {

    private val newWordActivityRequestCode = 1
    private lateinit var noteViewModel: NoteViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_note)

        val adapter = NoteListAdapter(this, intent.getLongExtra("tag", -1) ){

            val intent = Intent(this, ClickedActivity::class.java)
            intent.putExtra("tag", it.note)
            startActivity(intent)

        }

        recyclerview1.adapter = adapter
        recyclerview1.layoutManager = LinearLayoutManager(this)
        noteViewModel = ViewModelProvider(this).get(NoteViewModel::class.java)

        noteViewModel.allNotes.observe(this, Observer {
            adapter.setNotes(it)
        })

        val fab = findViewById<FloatingActionButton>(R.id.fab)
        fab.setOnClickListener {
            val intent = Intent(this, NewWordActivity::class.java)
            startActivityForResult(intent, newWordActivityRequestCode)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == newWordActivityRequestCode && resultCode == Activity.RESULT_OK)
        {
            data?.getStringArrayListExtra(NewWordActivity.EXTRA_REPLY)?.let {
                val note = Note(it[0], it[1], intent.getLongExtra("tag", -1))
                noteViewModel.insertNote(note)
            }
        }
        else
        {
            Toast.makeText(applicationContext, R.string.empty_not_saved,
                Toast.LENGTH_LONG).show()
        }
    }

Then, in the adapter, I use MutableMap to transform the list so that the key is the name id and the value is the notes selected on request (attached to a specific diary)然后,在适配器中,我使用 MutableMap 来转换列表,以便键是名称 id,值是根据请求选择的注释(附加到特定日记)

NoteListAdapter.kt: NoteListAdapter.kt:

class NoteListAdapter internal constructor(
    context: Context,
    val wordId: Long,
    private val listener : (Note) -> Unit
) : RecyclerView.Adapter<NoteListAdapter.NoteViewHolder>() {

    private val inflater: LayoutInflater = LayoutInflater.from(context)

    //private val mContext = context

    private var notes = emptyList<NotesAndWords>()   // Cached copy of words
    private var notesMapped = mutableMapOf<Long, List<Note>>()

    inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {


        private val noteItemView: TextView = itemView.findViewById(R.id.textView1)

        private val noteDescriptionView: TextView = itemView.findViewById(R.id.textView)

        fun bindView(note: Note, listener : (Note) -> Unit) {



                noteItemView.text = note.diaryId.toString()

                noteDescriptionView.text = note.text

            itemView.setOnClickListener {
                listener(note)
            }

        }

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {


        val itemView = inflater.inflate(R.layout.recyclerview_layout, parent,
            false)

        return NoteViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
        holder.bindView(notesMapped[wordId]!![position], listener)
    }

    internal fun setNotes(notes: List<NotesAndWords>) {
        this.notes = notes

        for (i in this.notes) {
            notesMapped[i.word.id] = i.notes
        }

        notifyDataSetChanged()
    }
    override fun getItemCount() = notesMapped[wordId]!!.size
}

Database:数据库:

@Database(entities = [Word::class, Note::class], version = 2, exportSchema = false)
abstract class WordRoomDatabase : RoomDatabase() {

    abstract fun wordDao(): WordDao

    private class WordDatabaseCallback(private val scope: CoroutineScope) : RoomDatabase.Callback()
    {

        override fun onOpen(db: SupportSQLiteDatabase) {
            super.onOpen(db)
            INSTANCE?.let { database ->
                scope.launch {
                    populateDatabase(database.wordDao())
                }
            }
        }

        suspend fun populateDatabase(wordDao: WordDao) {


            //wordDao.deleteAll()
            //wordDao.deleteAllNotes()

        }
    }

    companion object {

        @Volatile
        private var INSTANCE: WordRoomDatabase? = null

        fun getDatabase(context: Context, scope:CoroutineScope): WordRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            val instance = Room.databaseBuilder(context.applicationContext,
                WordRoomDatabase::class.java, "word_database")
                .addCallback(WordDatabaseCallback(scope))
                //.fallbackToDestructiveMigration()
                .build()
            INSTANCE = instance
            return instance
        }
    }
}

I've created several diaries and one note in each of them, using the buttons to create new diaries and notes.我创建了几本日记,每本中都有一个笔记,使用按钮来创建新的日记和笔记。 Now, if you select several diaries in turn, then on some attempt to select a diary, a NullPointerException is issued in the adapter, in this line:现在,如果您依次选择多个日记,然后在尝试选择日记时,适配器中会发出 NullPointerException,在这一行中:

override fun getItemCount() = notesMapped[wordId]!!.size

Why is this exception thrown if notesMapped always has the wordId key?如果 notesMapped 始终具有 wordId 键,为什么会引发此异常?

NoteActivity is called from another activity and the diary id is passed to it NoteActivity 是从另一个 Activity 调用的,并将日记 id 传递给它

This repository on GitHub: https://github.com/Lomank123/RoomDatabase GitHub 上的此存储库: https : //github.com/Lomank123/RoomDatabase

Edit:编辑:

        noteViewModel.allNotes.observe(this, Observer {
            var getList = emptyList<Note>()
            for(i in it)
            {
                if(i.word.id == wordId)
                {
                    getList = i.notes
                    break
                }
            }

            adapter.setNotes(getList)
        })

I've changed the Observer in NoteActivity and changed setNotes() method in adapter, but now it returns nothing.我已经更改了 NoteActivity 中的观察者并更改了适配器中的 setNotes() 方法,但现在它什么都不返回。 With for() I get the right notes and give them to adapter.setNotes().使用 for() 我得到正确的笔记并将它们交给 adapter.setNotes()。 If it doesn't work, how can I get the correct list of notes?如果它不起作用,我如何获得正确的笔记列表?

Hi initially the map is empty and the map is returning a null value and you are checking size on a null object.嗨,最初地图为空,地图返回空值,您正在检查空对象的大小。 Also as a good practice do not use a map instead use a list of notes only and pass the list directly.同样作为一个好习惯,不要使用地图,而是仅使用笔记列表并直接传递列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM