Один-ко-многим в номере с Kotlin - PullRequest
0 голосов
/ 02 марта 2020

Задача состоит в том, чтобы открыть действие с примечаниями, прикрепленными к этому дневнику, когда вы выбираете один дневник. (один ко многим) Вот как выглядят сущности в базе данных:

@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
    }

Использование класса данных в NoteRepository.kt

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

и запрос в WordDao.kt

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

Я получаю данные и сохраняю их в классе NoteRepository:

class NoteRepository (private val wordDao : WordDao) {

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

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

Затем через 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)

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()
        }
    }

Затем в адаптере я использую MutableMap для преобразования списка таким образом, чтобы ключ представлял собой идентификатор имени, а значение - примечания, выбранные по запросу (прикрепленные к c дневник)

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(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
        }
    }
}

Я создал несколько дневников и одну заметку в каждом из них, используя кнопки для создания новых дневников и заметок. Теперь, если вы выбираете несколько дневников по очереди, то при некоторой попытке выбрать дневник в адаптере выдается исключение NullPointerException в следующей строке:

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

Почему выбрасывается это исключение, если notesMapped всегда имеет ключ wordId?

Примечание. Активность вызывается из другого действия, и ему передается идентификатор дневника

Этот репозиторий на GitHub: https://github.com/Lomank123/RoomDatabase

Редактировать :

        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)
        })

Я изменил Observer в NoteActivity и изменил метод setNotes () в адаптере, но теперь он ничего не возвращает. С помощью for () я получаю правильные заметки и передаю их в adapter.setNotes (). Если это не работает, как я могу получить правильный список заметок?

1 Ответ

0 голосов
/ 02 марта 2020

Привет, изначально карта пуста, карта возвращает нулевое значение, и вы проверяете размер на нулевом объекте. Также не рекомендуется использовать карту, а использовать только список заметок и передавать список напрямую.

...