当我按下删除按钮时,我的应用程序崩溃 "sometimes"
my app crashes "sometimes" when i press delete button
所以我正在构建用于学习目的的列表应用程序,到目前为止我已经制作了 recyclerview,添加了新项目。我现在正在尝试为 recyclerview 项目实施 multi select selecting 并在 selecting 多个项目后删除它们。项目保存在房间数据库中。我正在使用 selection 跟踪器库来获取 selected recyclerview 项目 ID,然后我开始删除具有这些 ID 的项目。
现在的问题是,当我启动这个应用程序时,这个删除按钮有时会起作用,但在尝试删除更多项目后,它崩溃了。它需要重新启动应用程序才能再次工作,即便如此,它并不总是有效。
我一直在努力寻找解决办法,但到目前为止还没有找到。
如果有人能给我任何指示,我将不胜感激。image of my app. delete button is up in toolbar
我也是 android 开发的新手,如果您需要我的任何其他代码,请随时询问。
堆栈跟踪:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.todolist, PID: 9898
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 0(offset:-1).state:6 androidx.recyclerview.widget.RecyclerView{737e557 VFED..... ......ID 0,154-1080,1396 #7f080207 app:id/toDoRecyclerView}, adapter:com.example.todolist.Adapters.ToDoAdapter@2f75844, layout:androidx.recyclerview.widget.LinearLayoutManager@8cd7d2d, context:com.example.todolist.Activity.MainActivity@50574a3
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6183)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:675)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:4085)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3534)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:747)
at android.view.View.measure(View.java:25466)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3397)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2228)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2486)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:731)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
这是我的道:
interface NoteDao {
@Query("select * from notes")
fun getNotes() : LiveData<List<RoomNote>>
@Query("select * from notes where id = :id")
suspend fun getNoteById(id: Int) :RoomNote
@Delete
suspend fun deleteNote(note: RoomNote)
@Query("delete from notes where id = :id")
suspend fun deleteSingleItem(id: Int)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertOrUpdateNote(note: RoomNote)
}
数据class:
@Entity(
tableName = "notes"
)
data class RoomNote(
@ColumnInfo(name = "creation_date")
val creationDate: String,
@ColumnInfo(name = "title")
val title: String,
@ColumnInfo(name = "contents")
val contents: String,
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int
)
查看模型:
class ToDoViewModel(private val repository: ToDoRepository) :ViewModel(){
//get all notes
val allNotes : LiveData<List<RoomNote>> = repository.allNotes
//add new item
suspend fun insert(note: RoomNote) = viewModelScope.launch{
repository.insert(note)
}
//get single note
fun getSingleNote(id: Int) = viewModelScope.launch{
repository.getSingleNote(id)
}
// delete note
suspend fun deleteNote(note: RoomNote) = viewModelScope.launch {
repository.deleteNote(note)
}
suspend fun deleteSingleItem(id: Int) = viewModelScope.launch(Dispatchers.IO) {
repository.deleteSingleItem(id)
}
}
class ToDoViewModelFactory(private val repository: ToDoRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ToDoViewModel::class.java)) {
return ToDoViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
存储库:
class ToDoRepository(private val noteDao: NoteDao) {
val allNotes : LiveData<List<RoomNote>> = noteDao.getNotes()
@WorkerThread
suspend fun insert(note: RoomNote){
noteDao.insertOrUpdateNote(note)
}
@WorkerThread
suspend fun getSingleNote(id: Int) {
noteDao.getNoteById(id)
}
suspend fun deleteNote(note: RoomNote){
noteDao.deleteNote(note)
}
suspend fun deleteSingleItem(id: Int){
noteDao.deleteSingleItem(id)
}
}
activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var adapter: ToDoAdapter
private var tracker: SelectionTracker<Long>? = null
var itemsSelected = mutableListOf<Long>()
private val listViewModel:ToDoViewModel by viewModels{
ToDoViewModelFactory((application as ToDoApplication).repository)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
listViewModel.allNotes.observe(this, { newData ->
adapter.submitList(newData)
})
setSupportActionBar(binding.appBar)
init()
trackSelectedItems()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
binding.appBar.inflateMenu(R.menu.main_activity_toolbar_menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.addNewItem -> {
val intent = Intent(this, AddNewItem::class.java)
startActivity(intent)
true
}
R.id.deleteItemsButton ->{
launchDeletion()
d("clicked","clicked")
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
private fun init() {
adapter = ToDoAdapter()
binding.toDoRecyclerView.layoutManager = LinearLayoutManager(this)
binding.toDoRecyclerView.adapter = adapter
}
private fun trackSelectedItems() {
tracker = SelectionTracker.Builder<Long>(
"selection-1",
binding.toDoRecyclerView,
StableIdKeyProvider(binding.toDoRecyclerView),
ItemLookup(binding.toDoRecyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build()
adapter.setTracker(tracker)
tracker?.addObserver(object: SelectionTracker.SelectionObserver<Long>() {
override fun onSelectionChanged() {
//handle the selected according to your logic
itemsSelected.clear()
itemsSelected.addAll(tracker!!.selection)
d("itemsSelected","$itemsSelected}")
}
})
}
private fun launchDeletion() = runBlocking {
launch {
itemsSelectedDeletion() }
}
private suspend fun itemsSelectedDeletion(){
for (i in itemsSelected){
listViewModel.deleteSingleItem(i.toInt())
}
}
inner class ItemLookup(private val rv: RecyclerView)
: ItemDetailsLookup<Long>() {
override fun getItemDetails(event: MotionEvent)
: ItemDetails<Long>? {
val view = rv.findChildViewUnder(event.x, event.y)
if(view != null) {
return (rv.getChildViewHolder(view) as ViewHolder).getItemDetails()
}
return null
}
}
}
适配器
class ToDoAdapter: ListAdapter<RoomNote, ViewHolder>(WordsComparator()) {
private var tracker: SelectionTracker<Long>? = null
fun setTracker(tracker: SelectionTracker<Long>?) {
this.tracker = tracker
}
init {
setHasStableIds(true)
}
override fun getItemId(position: Int): Long = position.toLong()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.create(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val current = getItem(position)
holder.title.text = current.title
holder.creationDate.text = current.creationDate
tracker?.let {
if (it.isSelected(position.toLong())) {
it.select(position.toLong())
holder.marked.alpha = 1.0F
} else {
it.deselect(position.toLong())
holder.marked.alpha = 0.0F
}
}
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long = itemId
}
val title: TextView = itemView.findViewById(R.id.title)
val creationDate: TextView = itemView.findViewById(R.id.creationDate)
val marked: Button = itemView.findViewById(R.id.checkButton)
companion object {
fun create(parent: ViewGroup): ViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.todo_item, parent, false)
return ViewHolder(view)
}
}
}
class WordsComparator : DiffUtil.ItemCallback<RoomNote>() {
override fun areContentsTheSame(oldItem: RoomNote, newItem: RoomNote): Boolean {
return oldItem.id == newItem.id
}
override fun areItemsTheSame(oldItem: RoomNote, newItem: RoomNote): Boolean {
return oldItem == newItem
}
}
工具栏菜单:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/deleteItemsButton"
android:icon="@mipmap/delete_button"
android:title="plus"
app:showAsAction="always"
/>
<item
android:id="@+id/addNewItem"
android:icon="@mipmap/plus_icon"
android:title="plus"
app:showAsAction="always"
/>
</menu>
在你的适配器中你有:
setHasStableIds(true)
和
override fun getItemId(position: Int): Long = position.toLong()
这是矛盾的。当项目被删除时,位置和ids会发生变化,使ids不稳定。
要么删除 setHasStableIds(true)
,要么想出一种方法来生成实际稳定的项目 ID(例如基于数据中的 id
字段)。
所以我正在构建用于学习目的的列表应用程序,到目前为止我已经制作了 recyclerview,添加了新项目。我现在正在尝试为 recyclerview 项目实施 multi select selecting 并在 selecting 多个项目后删除它们。项目保存在房间数据库中。我正在使用 selection 跟踪器库来获取 selected recyclerview 项目 ID,然后我开始删除具有这些 ID 的项目。
现在的问题是,当我启动这个应用程序时,这个删除按钮有时会起作用,但在尝试删除更多项目后,它崩溃了。它需要重新启动应用程序才能再次工作,即便如此,它并不总是有效。 我一直在努力寻找解决办法,但到目前为止还没有找到。 如果有人能给我任何指示,我将不胜感激。image of my app. delete button is up in toolbar 我也是 android 开发的新手,如果您需要我的任何其他代码,请随时询问。
堆栈跟踪:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.todolist, PID: 9898
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 0(offset:-1).state:6 androidx.recyclerview.widget.RecyclerView{737e557 VFED..... ......ID 0,154-1080,1396 #7f080207 app:id/toDoRecyclerView}, adapter:com.example.todolist.Adapters.ToDoAdapter@2f75844, layout:androidx.recyclerview.widget.LinearLayoutManager@8cd7d2d, context:com.example.todolist.Activity.MainActivity@50574a3
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6183)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:675)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:4085)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3534)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:747)
at android.view.View.measure(View.java:25466)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3397)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2228)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2486)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:731)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
这是我的道:
interface NoteDao {
@Query("select * from notes")
fun getNotes() : LiveData<List<RoomNote>>
@Query("select * from notes where id = :id")
suspend fun getNoteById(id: Int) :RoomNote
@Delete
suspend fun deleteNote(note: RoomNote)
@Query("delete from notes where id = :id")
suspend fun deleteSingleItem(id: Int)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertOrUpdateNote(note: RoomNote)
}
数据class:
@Entity(
tableName = "notes"
)
data class RoomNote(
@ColumnInfo(name = "creation_date")
val creationDate: String,
@ColumnInfo(name = "title")
val title: String,
@ColumnInfo(name = "contents")
val contents: String,
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int
)
查看模型:
class ToDoViewModel(private val repository: ToDoRepository) :ViewModel(){
//get all notes
val allNotes : LiveData<List<RoomNote>> = repository.allNotes
//add new item
suspend fun insert(note: RoomNote) = viewModelScope.launch{
repository.insert(note)
}
//get single note
fun getSingleNote(id: Int) = viewModelScope.launch{
repository.getSingleNote(id)
}
// delete note
suspend fun deleteNote(note: RoomNote) = viewModelScope.launch {
repository.deleteNote(note)
}
suspend fun deleteSingleItem(id: Int) = viewModelScope.launch(Dispatchers.IO) {
repository.deleteSingleItem(id)
}
}
class ToDoViewModelFactory(private val repository: ToDoRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ToDoViewModel::class.java)) {
return ToDoViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
存储库:
class ToDoRepository(private val noteDao: NoteDao) {
val allNotes : LiveData<List<RoomNote>> = noteDao.getNotes()
@WorkerThread
suspend fun insert(note: RoomNote){
noteDao.insertOrUpdateNote(note)
}
@WorkerThread
suspend fun getSingleNote(id: Int) {
noteDao.getNoteById(id)
}
suspend fun deleteNote(note: RoomNote){
noteDao.deleteNote(note)
}
suspend fun deleteSingleItem(id: Int){
noteDao.deleteSingleItem(id)
}
}
activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var adapter: ToDoAdapter
private var tracker: SelectionTracker<Long>? = null
var itemsSelected = mutableListOf<Long>()
private val listViewModel:ToDoViewModel by viewModels{
ToDoViewModelFactory((application as ToDoApplication).repository)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
listViewModel.allNotes.observe(this, { newData ->
adapter.submitList(newData)
})
setSupportActionBar(binding.appBar)
init()
trackSelectedItems()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
binding.appBar.inflateMenu(R.menu.main_activity_toolbar_menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.addNewItem -> {
val intent = Intent(this, AddNewItem::class.java)
startActivity(intent)
true
}
R.id.deleteItemsButton ->{
launchDeletion()
d("clicked","clicked")
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
private fun init() {
adapter = ToDoAdapter()
binding.toDoRecyclerView.layoutManager = LinearLayoutManager(this)
binding.toDoRecyclerView.adapter = adapter
}
private fun trackSelectedItems() {
tracker = SelectionTracker.Builder<Long>(
"selection-1",
binding.toDoRecyclerView,
StableIdKeyProvider(binding.toDoRecyclerView),
ItemLookup(binding.toDoRecyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build()
adapter.setTracker(tracker)
tracker?.addObserver(object: SelectionTracker.SelectionObserver<Long>() {
override fun onSelectionChanged() {
//handle the selected according to your logic
itemsSelected.clear()
itemsSelected.addAll(tracker!!.selection)
d("itemsSelected","$itemsSelected}")
}
})
}
private fun launchDeletion() = runBlocking {
launch {
itemsSelectedDeletion() }
}
private suspend fun itemsSelectedDeletion(){
for (i in itemsSelected){
listViewModel.deleteSingleItem(i.toInt())
}
}
inner class ItemLookup(private val rv: RecyclerView)
: ItemDetailsLookup<Long>() {
override fun getItemDetails(event: MotionEvent)
: ItemDetails<Long>? {
val view = rv.findChildViewUnder(event.x, event.y)
if(view != null) {
return (rv.getChildViewHolder(view) as ViewHolder).getItemDetails()
}
return null
}
}
}
适配器
class ToDoAdapter: ListAdapter<RoomNote, ViewHolder>(WordsComparator()) {
private var tracker: SelectionTracker<Long>? = null
fun setTracker(tracker: SelectionTracker<Long>?) {
this.tracker = tracker
}
init {
setHasStableIds(true)
}
override fun getItemId(position: Int): Long = position.toLong()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.create(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val current = getItem(position)
holder.title.text = current.title
holder.creationDate.text = current.creationDate
tracker?.let {
if (it.isSelected(position.toLong())) {
it.select(position.toLong())
holder.marked.alpha = 1.0F
} else {
it.deselect(position.toLong())
holder.marked.alpha = 0.0F
}
}
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long = itemId
}
val title: TextView = itemView.findViewById(R.id.title)
val creationDate: TextView = itemView.findViewById(R.id.creationDate)
val marked: Button = itemView.findViewById(R.id.checkButton)
companion object {
fun create(parent: ViewGroup): ViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.todo_item, parent, false)
return ViewHolder(view)
}
}
}
class WordsComparator : DiffUtil.ItemCallback<RoomNote>() {
override fun areContentsTheSame(oldItem: RoomNote, newItem: RoomNote): Boolean {
return oldItem.id == newItem.id
}
override fun areItemsTheSame(oldItem: RoomNote, newItem: RoomNote): Boolean {
return oldItem == newItem
}
}
工具栏菜单:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/deleteItemsButton"
android:icon="@mipmap/delete_button"
android:title="plus"
app:showAsAction="always"
/>
<item
android:id="@+id/addNewItem"
android:icon="@mipmap/plus_icon"
android:title="plus"
app:showAsAction="always"
/>
</menu>
在你的适配器中你有:
setHasStableIds(true)
和
override fun getItemId(position: Int): Long = position.toLong()
这是矛盾的。当项目被删除时,位置和ids会发生变化,使ids不稳定。
要么删除 setHasStableIds(true)
,要么想出一种方法来生成实际稳定的项目 ID(例如基于数据中的 id
字段)。