更新数据库中的项目后,列表适配器未更新

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

我正在使用 RoomDB 编写一个笔记应用程序,当我尝试更新现有笔记时,它会在数据库中完美更新,但我必须重新启动应用程序或打开另一个片段,然后返回 Recycler 视图才能更新

这是我的适配器:

class RVNotesAdapter : ListAdapter<Note, RVNotesAdapter.NoteViewHolder>(DiffUtilCallback()) {


    inner class NoteViewHolder(item: View) : RecyclerView.ViewHolder(item) {
        val binding = NoteItemLayoutBinding.bind(item)
        val title = binding.noteItemTitle
        val content = binding.noteContentItem
        val date = binding.noteDate
        val parent = binding.noteItemLayoutParent
        val markWon = Markwon.builder(item.context)
            .usePlugin(StrikethroughPlugin.create())
            .usePlugin(TaskListPlugin.create(item.context))
            .usePlugin(object : AbstractMarkwonPlugin() {
                override fun configureVisitor(builder: MarkwonVisitor.Builder) {
                    super.configureVisitor(builder)
                    builder.on(
                        SoftLineBreak::class.java
                    ) { visitor, _ ->
                        visitor.forceNewLine()
                    }
                }
            }).build()
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
        return NoteViewHolder(LayoutInflater.from(parent.context)
            .inflate(R.layout.note_item_layout,parent,false))
    }

    override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
        getItem(position).let {note->
            holder.apply {
                parent.transitionName = "recyclerView_${note.id}"
                title.text = note.title
                markWon.setMarkdown(content,note.content)
                date.text = note.date
                parent.setCardBackgroundColor(note.color)

                itemView.setOnClickListener {
                    val action = NoteFragmentDirections.actionNoteFragmentToSaveOrUpdateFragment()
                        .setNote(note)

                    val extras = FragmentNavigatorExtras(parent to "recyclerView_${note.id}")
                    it.hideKeyboard()
                    Navigation.findNavController(it).navigate(action,extras)
                }

                content.setOnClickListener {
                    val action = NoteFragmentDirections.actionNoteFragmentToSaveOrUpdateFragment()
                        .setNote(note)

                    val extras = FragmentNavigatorExtras(parent to "recyclerView_${note.id}")
                    it.hideKeyboard()
                    Navigation.findNavController(it).navigate(action,extras)
                }
            }
        }
    }
}

这是 DiffUtil 类:

class DiffUtilCallback : DiffUtil.ItemCallback<Note>() {
    override fun areItemsTheSame(oldItem: Note, newItem: Note): Boolean {
        return oldItem.id == newItem.id
    }
    override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean {
        return oldItem.id == newItem.id
    }
}

这是注释片段:

@AndroidEntryPoint
class NoteFragment : Fragment(R.layout.fragment_note) {

    private lateinit var binding: FragmentNoteBinding
    private val noteActivityViewModel by viewModels<NoteActivityViewModel>()
    private lateinit var rvNotesAdapter: RVNotesAdapter

   
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentNoteBinding.bind(view)
        val activity = activity as MainActivity
        val navController = Navigation.findNavController(view)
        requireView().hideKeyboard()
        CoroutineScope(Dispatchers.Main).launch {
            delay(10)
//            activity.window.statusBarColor = Color.WHITE
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            activity.window.statusBarColor = Color.parseColor("#9e9d9d")
        }





        binding.addNoteFab.setOnClickListener {
            binding.appBar.visibility = INVISIBLE
            navController.navigate(NoteFragmentDirections.actionNoteFragmentToSaveOrUpdateFragment())
        }

        binding.innerFab.setOnClickListener {
            binding.appBar.visibility = INVISIBLE
            navController.navigate(NoteFragmentDirections.actionNoteFragmentToSaveOrUpdateFragment())
        }

        recyclerViewDisplay()
        swipeToDelete(binding.rvNote)


        // implement search
        binding.search.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
                binding.noData.isVisible = false
            }

            override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
                if (s.toString().isNotEmpty()) {
                    val text = s.toString()
                    val query = "%$text%"
                    if (query.isNotEmpty()) {
                        noteActivityViewModel.searchNote(query).observe(viewLifecycleOwner) {
                            rvNotesAdapter.submitList(it)
                        }
                    } else {
                        observerDataChanges()
                    }
                } else {
                    observerDataChanges()
                }
            }


            override fun afterTextChanged(p0: Editable?) {

            }

        })

        binding.search.setOnEditorActionListener { v, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                v.clearFocus()
                requireView().hideKeyboard()
            }

            return@setOnEditorActionListener true
        }

        binding.rvNote.setOnScrollChangeListener { _, scrollX, scrollY, _, oldscrollY ->

            when {
                scrollY > oldscrollY -> {
                    binding.fabText.isVisible = false
                }

                scrollX == scrollY -> {
                    binding.fabText.isVisible = true
                }

                else -> {
                    binding.fabText.isVisible = true
                }
            }
        }


    }

    private fun swipeToDelete(rvNote: RecyclerView) {
        val swipeToDeleteCallBack = object : SwipeToDelete() {
            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                val position = viewHolder.absoluteAdapterPosition
                val note = rvNotesAdapter.currentList[position]
                var actionBTNTab = false
                noteActivityViewModel.deleteNote(note)
                binding.search.apply {
                    hideKeyboard()
                    clearFocus()
                }

                if (binding.search.toString().isEmpty()) {
                    observerDataChanges()
                }

                val snackBar = Snackbar.make(
                    requireView(),
                    "یادداشت حذف شد",
                    Snackbar.LENGTH_SHORT
                )
                    .addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
                        override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
                            super.onDismissed(transientBottomBar, event)

                        }

                        override fun onShown(transientBottomBar: Snackbar?) {
                            transientBottomBar?.setAction("برگرد") {
                                noteActivityViewModel.addNote(note)
                                actionBTNTab = true
                                binding.noData.isVisible = false
                            }
                            super.onShown(transientBottomBar)
                        }
                    })
                    .apply {
                        animationMode = Snackbar.ANIMATION_MODE_SLIDE
                        setAnchorView(R.id.add_note_fab)
                    }
                snackBar.setActionTextColor(
                    ContextCompat.getColor(
                        requireContext(),
                        R.color.yellowOrange
                    )
                )
                    .show()
            }

        }

        val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallBack)
        itemTouchHelper.attachToRecyclerView(rvNote)
    }

    private fun recyclerViewDisplay() {
        when (resources.configuration.orientation) {
            Configuration.ORIENTATION_PORTRAIT -> {
                setUpRecyclerView(2)
            }

            Configuration.ORIENTATION_LANDSCAPE -> {
                setUpRecyclerView(3)
            }
        }
    }

    private fun setUpRecyclerView(spanCount: Int) {
        binding.rvNote.apply {
            layoutManager =
                StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL)
            setHasFixedSize(true)
            rvNotesAdapter = RVNotesAdapter()
            rvNotesAdapter.stateRestorationPolicy =
                RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
            adapter = rvNotesAdapter
            postponeEnterTransition(300L, TimeUnit.MILLISECONDS)
            viewTreeObserver.addOnPreDrawListener {
                startPostponedEnterTransition()
                true
            }
        }
        observerDataChanges()
    }

    private fun observerDataChanges() {
        noteActivityViewModel.allNotes.observe(viewLifecycleOwner) { list ->
            binding.noData.isVisible = list.isEmpty()
            rvNotesAdapter.submitList(list)
        }
    }

}

这是 saveOrUpdate Fragment 的所有代码:


@AndroidEntryPoint
class SaveOrUpdateFragment : Fragment(R.layout.fragment_save_or_update) {

 private lateinit var navController: NavController
    private lateinit var binding: FragmentSaveOrUpdateBinding
    private var note: Note? = null
    private var color = -1
    private lateinit var result: String
    private val noteActivityViewModel by viewModels<NoteActivityViewModel>()
    private val currentDate = SimpleDateFormat.getInstance().format(Date())
    private val job = CoroutineScope(Dispatchers.Main)
    private val args: SaveOrUpdateFragmentArgs by navArgs()
    private lateinit var rvNotesAdapter: RVNotesAdapter

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val animation = MaterialContainerTransform().apply {
            drawingViewId = R.id.fragment
            scrimColor = Color.TRANSPARENT
            duration = 300
        }

        sharedElementEnterTransition = animation
        sharedElementReturnTransition = animation
    }


    @SuppressLint("SetTextI18n")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentSaveOrUpdateBinding.bind(view)



        navController = Navigation.findNavController(view)
        val activity = activity as MainActivity


        ViewCompat.setTransitionName(
            binding.noteContentFragParent,
            "recyclerView_${args.note?.id}"
        )

        binding.backButton.setOnClickListener {
            requireView().hideKeyboard()
            navController.popBackStack()
        }




        binding.saveNote.setOnClickListener {
            saveNote()
        }

        try {
            binding.etNoteContent.setOnFocusChangeListener { view, hasFocus ->
                if (hasFocus) {
                    binding.bottomBar.visibility = View.VISIBLE
                    binding.etNoteContent.setStylesBar(binding.styleBar)
                } else {
                    binding.bottomBar.visibility = View.GONE
                }
            }
        } catch (e: Exception) {
            Log.e("TAG", "$e")
        }

        binding.fabColorPicker.setOnClickListener {
            val bottomSheetDialog = BottomSheetDialog(
                requireContext(),
                R.style.BottomSheetDialogTheme
            )


            val bottomSheetView: View = layoutInflater.inflate(R.layout.bottom_sheet_layout, null)

            with(bottomSheetDialog) {
                setContentView(bottomSheetView)
                show()
            }

            val bottomSheetBinding = BottomSheetLayoutBinding.bind(bottomSheetView)
            bottomSheetBinding.apply {
                colorPicker.apply {
                    setSelectedColor(color)
                    setOnColorSelectedListener { value ->
                        color = value
                        binding.apply {
                            noteContentFragParent.setBackgroundColor(color)
                            toolbarFragNoteContent.setBackgroundColor(color)
                            bottomBar.setBackgroundColor(color)
                            activity.window.statusBarColor = color
                        }
                        bottomSheetBinding.bottomSheetParent.setCardBackgroundColor(color)
                    }
                }
                bottomSheetParent.setCardBackgroundColor(color)
            }
            bottomSheetView.post {
                bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
            }
        }

        // opens existing item
        setUpNote()

    }

    @SuppressLint("SetTextI18n")
    private fun setUpNote() {
        val note = args.note
        val title = binding.etTitle
        val content = binding.etNoteContent
        val lastEdited = binding.lastEdited

        if (note == null){
            binding.lastEdited.text =
                "${getString(R.string.edited_on)}: ${SimpleDateFormat.getDateInstance().format(Date())}"
        }
        if (note!=null){
            title.setText(note.title)
            content.renderMD(note.content)
            lastEdited.text = "${getString(R.string.edited_on)}: ${note.date}"
            color = note.color
            binding.apply {
                job.launch {
                    delay(10)
                    noteContentFragParent.setBackgroundColor(color)
                }

                toolbarFragNoteContent.setBackgroundColor(color)
                bottomBar.setBackgroundColor(color)

            }
            activity?.window?.statusBarColor = note.color
        }
    }

    private fun saveNote() {
        if (binding.etNoteContent.text.toString().isEmpty() || binding.etTitle.text.toString()
                .isEmpty()
        ) {
            Toast.makeText(activity, "یادداشت نباید خالی باشد", Toast.LENGTH_SHORT).show()
        } else {
            note = args.note

            when (note) {
                null -> {
                    noteActivityViewModel.addNote(
                        Note(
                            0,
                            title = binding.etTitle.text.toString(),
                            content = binding.etNoteContent.getMD(),
                            color = color,
                            date = currentDate
                        )
                    )

                    result = "ذخیره شد"
                    setFragmentResult(
                        "key",
                        bundleOf("bundleKey" to result)
                    )

                    navController.navigate(SaveOrUpdateFragmentDirections.actionSaveOrUpdateFragmentToNoteFragment())
                }

                else -> {
                    updateNote()
                    navController.popBackStack()
                }
            }
        }
    }

    private fun updateNote() {
        if (note != null) {
            noteActivityViewModel.updateNote(
                Note(
                    id = note!!.id,
                    content = binding.etNoteContent.getMD(),
                    title = binding.etTitle.text.toString(),
                    date = currentDate,
                    color = color
                )
            )
        }
    }


}

我将不胜感激任何帮助。

kotlin android-fragments android-recyclerview android-adapter listadapter
1个回答
0
投票

您的问题在于您的

diffUtil
课程。

简短回答:

areContentsTheSame
更改为:

override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean {
        return oldItem == newItem
    }

长答案:

DiffUtil
有两种方法:

  • areItemsTheSame
    :当比较两个列表中的项目时,首先调用此函数,以便它知道两个项目是否相同(然后检查更改)。所以在这里你需要检查他们的唯一标识符,比如
    id
    ,你做得是否正确。
  • areContentsTheSame
    :这个叫做 after 前一个返回 true,它要你比较这些类的内容,看看它们是否改变了。

第二个是你做对的,正如我提到的,它仅在前一个函数返回 true 时被调用,因为你正在检查 ids,那么 ids 肯定会是相同的(这也是你在这里检查的条件) )所以项目不会改变。

这里需要检查类的Content是否发生了变化,最常用的两种方式是hashCode比较

oldItem.hashCode() == newItem.hashCode()
或相等性比较
oldItem == newItem
。 尽管如果您的类中有一些数据发生变化但未显示在用户界面中(并且您有很多字段),我建议您进行更详细的检查:

return oldItem.content == newItem.content &&
    oldItem.title == newItem.title &&
    oldItem.color == newItem.color

请注意,使用自定义相等性检查意味着如果您添加与数据类相关的 UI,您也需要在此处添加它,因此 ui 会更新

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