【发布时间】:2020-06-05 11:17:12
【问题描述】:
问题
滑动 filtered 项目(使用 SearchView)时,滑动删除可删除错误条目。在上面的 gif 中,您可以看到删除了“Item 3”...“Item 1”而不是“Item 1”。不使用 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)
}
}
【问题讨论】:
标签: kotlin mvvm android-recyclerview android-room android-livedata