问题
滑动“删除”会在滑动已过滤项时(使用SearchView)删除错误的条目。在上面的gif中,您可以看到删除了“项目1”而不是“项目3”。不使用SearchView时,滑动删除即可正常工作。
场景
列出项目:“项目1”,“项目2”和“项目3”。
我可以成功滑动以删除所有条目。我在SearchView字段中输入“ 3”。一切正常,现在我只看到列表中的“项目3”从位置2移到了位置0。不幸的是,当我滑动“项目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)
}
}
答案 0 :(得分:0)
我正在创建适配器的多个实例。我不得不在有趣的getItemsFromDb中删除了这三行:
MainActivity.kt
private fun getItemsFromDb(searchText: String) {
val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
val adapter = WordListAdapter(this)
recyclerView.adapter = adapter
并将适配器设置为MaintActivity.kt中的成员变量:
私有lateinit var适配器:WordListAdapter