Android Words示例(MVVM,LiveData,会议室)+ SearchView +删除,删除错误的项目

问题描述 投票:0回答:1

GIF SHOWING THE ISSUE

问题滑动“删除”会在滑动已过滤项目时(使用SearchView)删除错误的输入。在上面的gif中,您可以看到删除了“项目1”而不是“项目3”。不使用SearchView时,滑动删除即可正常工作。

场景列出项目:“项目1”,“项目2”和“项目3”。我可以成功滑动以删除每个条目。我在SearchView字段中输入“ 3”。一切正常,现在我只在列表中从位置2移到位置0看到“项目3”。不幸的是,当我滑动“项目3” ...时,“项目1”被删除。

[我花了多个小时尝试解决这个问题,并对其进行处理。我只是不知道我在这里缺少什么以及如何校正适配器位置值(我认为应该归咎于适配器位置)。

我非常感谢您的帮助,提示等。>

背景]我试图了解MVVM模型。我使用了Google room with a view example

我添加: -滑动删除 -自动生成的Int主键 -有趣的deleteWord,有趣的searchForItems -有趣的getWordAtPosition(检测滑动单词) -带搜索按钮的操作栏菜单 -删除项目时的小吃店

MainActivity.kt

class MainActivity : AppCompatActivity() {

private val newWordActivityRequestCode = 1
private lateinit var wordViewModel: WordViewModel
    private lateinit var searchView: SearchView

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

    val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
    val adapter = WordListAdapter(this)
    recyclerView.adapter = adapter
    recyclerView.layoutManager = LinearLayoutManager(this)

    val helper = ItemTouchHelper(
            object : ItemTouchHelper.SimpleCallback(0,
                    ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
                override fun onMove(recyclerView: RecyclerView,
                                    viewHolder: RecyclerView.ViewHolder,
                                    target: RecyclerView.ViewHolder): Boolean {
                    return false
                }

                override fun onSwiped(viewHolder: RecyclerView.ViewHolder,
                                      direction: Int) {
                    val position = viewHolder.adapterPosition
                    val myWord = adapter.getWordAtPosition(position)
                    Toast.makeText(this@MainActivity, "Deleting " +
                            myWord.word, Toast.LENGTH_LONG).show()

                    // Delete the word
                    wordViewModel.deleteWord(myWord)
                }
            })

    helper.attachToRecyclerView(recyclerView)

    // Get a new or existing ViewModel from the ViewModelProvider.
    wordViewModel = ViewModelProvider(this).get(WordViewModel::class.java)

    // Add an observer on the LiveData returned by getAlphabetizedWords.
    // The onChanged() method fires when the observed data changes and the activity is
    // in the foreground.
    wordViewModel.allWords.observe(this, Observer { words ->
        // Update the cached copy of the words in the adapter.
        words?.let { adapter.setWords(it) }
    })

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

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

    if (requestCode == newWordActivityRequestCode && resultCode == Activity.RESULT_OK) {
        intentData?.let { data ->
            val word = Word(null, data.getStringExtra(NewWordActivity.EXTRA_REPLY))
            wordViewModel.insert(word)
            Unit
        }
    } else {
        Toast.makeText(
                applicationContext,
                R.string.empty_not_saved,
                Toast.LENGTH_LONG
        ).show()
    }
}


override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.main_menu, menu)

    val search = menu.findItem(R.id.searchItems)
    searchView = search.actionView as androidx.appcompat.widget.SearchView
    searchView.isSubmitButtonEnabled = false
    searchView.setOnQueryTextListener(object : androidx.appcompat.widget.
    SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String?): Boolean {
            if (query != null) {
                getItemsFromDb(query)
            }
            return true
        }

        override fun onQueryTextChange(newText: String?): Boolean {
            if (newText != null) {
                getItemsFromDb(newText)
            }
            return true
        }

    })

    return true
}


private fun getItemsFromDb(searchText: String) {
    val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
    val adapter = WordListAdapter(this)
    recyclerView.adapter = adapter

    var searchText = searchText
    searchText = "%$searchText%"

    wordViewModel.searchForItems(desc = searchText).observe(this@MainActivity, Observer { words ->
        words?.let {

            Log.e("List = ", words.toString())
            adapter.setWords(it)
        }
    })
}

}

WordListAdapter.kt

class WordListAdapter internal constructor(context: Context) : RecyclerView.Adapter<WordListAdapter.WordViewHolder>() {

private val inflater: LayoutInflater = LayoutInflater.from(context)
private var words = emptyList<Word>() // Cached copy of words

inner class WordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val wordItemView: TextView = itemView.findViewById(R.id.textView)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder {
    val itemView = inflater.inflate(R.layout.recyclerview_item, parent, false)
    return WordViewHolder(itemView)
}

override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
    val current = words[position]
    holder.wordItemView.text = current.word
}

internal fun setWords(words: List<Word>) {
    this.words = words
    notifyDataSetChanged()
}

override fun getItemCount() = words.size

fun getWordAtPosition(position: Int): Word {
    return words[position]
}

}

WordViewModel.kt

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

private val repository: WordRepository
// 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 allWords: LiveData<List<Word>>

init {
    val wordsDao = WordRoomDatabase.getDatabase(application, viewModelScope).wordDao()
    repository = WordRepository(wordsDao)
    allWords = repository.allWords
}

/**
 * Launching a new coroutine to insert the data in a non-blocking way
 */
fun insert(word: Word) = viewModelScope.launch(Dispatchers.IO) {
    repository.insert(word)
}

fun deleteWord(word: Word) = viewModelScope.launch(Dispatchers.IO) {
    repository.deleteWord(word)
}

fun searchForItems(desc: String): LiveData<List<Word>> {
    return repository.search(desc)
}

}

WordDao.kt

@Dao interface WordDao {

@Query("SELECT * from word_table ORDER BY word ASC")
fun getAlphabetizedWords(): LiveData<List<Word>>

@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(word: Word)

@Query("DELETE FROM word_table")
fun deleteAll()

@Delete()
fun deleteWord(word: Word)

@Query("SELECT * FROM word_table WHERE word LIKE :desc")
fun getSearchResults(desc : String) : LiveData<List<Word>>

}

Word.kt

@Entity(tableName = "word_table")
data class Word(
        @PrimaryKey(autoGenerate = true) val id: Int? = 0,
        @ColumnInfo(name = "word") val word: String)

WordRepository.kt

class WordRepository(private val wordDao: WordDao) {
val allWords: LiveData<List<Word>> = wordDao.getAlphabetizedWords()

  @Suppress("RedundantSuspendModifier")
@WorkerThread
suspend fun insert(word: Word) {
    wordDao.insert(word)
}

fun deleteWord(word: Word) {
    wordDao.deleteWord(word)
}

fun search(desc : String) : LiveData<List<Word>>{
    return wordDao.getSearchResults(desc)
}

}

GIF显示问题发行版滑动过滤以删除时删除了错误的条目(使用SearchView)时,刷入已过滤的项目。在上面的gif中,您可以看到删除了“项目1”而不是“项目3”。滑动...

kotlin mvvm android-recyclerview android-room android-livedata
1个回答
0
投票

我正在创建适配器的多个实例。我不得不在有趣的getItemsFromDb中删除这三行:

MainActivity.kt

© www.soinside.com 2019 - 2024. All rights reserved.