问题滑动“删除”会在滑动已过滤项目时(使用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”。滑动...
我正在创建适配器的多个实例。我不得不在有趣的getItemsFromDb中删除这三行:
MainActivity.kt