【问题标题】:RecyclerView Overwrite last element a couple timesRecyclerView 覆盖最后一个元素几次
【发布时间】:2021-03-02 15:08:01
【问题描述】:

我正在使用 MVVC 模式,并且正在使用 Room 使用数据库中的数据填充 recyclerView。在 Logcat 处,数据正确返回并正确循环,但 recyclerview 显示七个元素,并在第八个开始用第九个和第十个元素覆盖它,然后创建 2列表中前 2 个元素的更多元素。

我找不到我的代码有什么问题。

所以,我正在寻求帮助。

AvaliacaoFragment.kt:

class AvaliacaoFragment : Fragment() {

    private lateinit var ctx: Context
    private var _binding: FragmentAvaliacaoBinding? = null
    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!
    private lateinit var textoSemSecoes: TextView
    private lateinit var nomeAvaliacao: TextView
    private lateinit var dataAvaliacao: TextView
    private val args by navArgs<AvaliacaoFragmentArgs>()


    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        _binding = FragmentAvaliacaoBinding.inflate(inflater, container, false)
        return binding.root
        // return inflater.inflate(R.layout.fragment_avaliacoes, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        ctx = view.context

        // dialogNovaAvaliacao = MaterialAlertDialogBuilder(ctx, android.R.style.Theme_DeviceDefault_Light_NoActionBar_Fullscreen)
        // dialogNovaAvaliacao = MaterialAlertDialogBuilder(ctx,R.style.AlertDialogTheme)
        // val builder = MaterialDatePicker.Builder.datePicker()

        textoSemSecoes = view.findViewById(R.id.texto_sem_secoes)
        nomeAvaliacao = view.findViewById(R.id.nome_avaliacao)
        dataAvaliacao = view.findViewById(R.id.data_avaliacao)

        nomeAvaliacao.text = args.currentAvaliacao.nome
        dataAvaliacao.text = args.currentAvaliacao.dataCriacao

        /*
        btnAddAvaliacao = view.findViewById(R.id.btn_add_avaliacao)
        btnAddAvaliacao.setOnClickListener {
            findNavController().navigate(R.id.action_navigation_avaliacoes_to_addAvaliacaoFragment)
        }
        */

        // Recycler
        val recyclerAdapter = SecaoAdapter()
        val recyclerView = binding.secaoRecyclerView
        recyclerView.adapter = recyclerAdapter
        recyclerView.layoutManager = LinearLayoutManager(requireContext())

        // ViewModelFactory para passar argumentos para a ViewModel
        val factory = object : ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                return SecaoViewModel(Application(), args.currentAvaliacao.id) as T
            }
        }

        // ViewModel
        mSecaoViewModel = ViewModelProvider(this, factory).get(SecaoViewModel::class.java)
        mSecaoViewModel.readAllData.observe(viewLifecycleOwner, Observer { secaoList ->
            if(secaoList.isNotEmpty()){
                Log.d(TAG, "secaoList: ${secaoList.toString()}")
                Log.d(TAG, "secaoList.size: ${secaoList.size}")
                textoSemSecoes.visibility = View.GONE
                recyclerView.visibility = View.VISIBLE
                recyclerAdapter.setData(secaoList.sortedBy { it.codigo.toInt() })
            } else {
                textoSemSecoes.visibility = View.VISIBLE
                recyclerView.visibility = View.GONE
            }
        })

    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    companion object {
        private val TAG: String = AvaliacaoFragment::class.java.name

        lateinit var mSecaoViewModel: SecaoViewModel
        private lateinit var btnAddSecao: FloatingActionButton
        private lateinit var dialogNovaSecao: MaterialAlertDialogBuilder

    }
}

SecaoAdapter.kt:

class SecaoAdapter: RecyclerView.Adapter<SecaoAdapter.SecaoViewHolder>() {

    private val TAG: String = SecaoAdapter::class.java.name
    private var secaoList = emptyList<Secao>()
    private lateinit var binding: ItemSecaoBinding

    class SecaoViewHolder(itemBinding: ItemSecaoBinding): RecyclerView.ViewHolder(itemBinding.root) {

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SecaoViewHolder {
        binding = ItemSecaoBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return SecaoViewHolder(binding)
    }

    override fun getItemCount(): Int {
        return secaoList.size
    }
    override fun onBindViewHolder(holder: SecaoViewHolder, position: Int) {
        Log.d(TAG, "position: $position")
        val currentItem = secaoList[position]
        Log.d(TAG, "currentItem: ${currentItem.toString()}")

        binding.secaoCodigo.text = currentItem.codigo
        binding.secaoNome.text = currentItem.nome
        binding.secaoMediaTotal.text = currentItem.mediaPositivo.toString()
        binding.secaoPerguntasNaoAplicaveis.text = currentItem.qdePerguntasNaoAplicaveis.toString()
        binding.secaoPerguntasRespondidas.text = currentItem.qdePerguntasRespondidas.toString()
        binding.secaoPerguntasTotais.text = currentItem.qdePerguntas.toString()

        binding.cardSecao.setOnClickListener {
            val action = AvaliacaoFragmentDirections.actionAvaliacaoFragmentToSecaoFragment(currentItem)
            holder.itemView.findNavController().navigate(action)
        }
    }

    fun setData(secao: List<Secao>){
        this.secaoList = secao
        notifyDataSetChanged()
    }
}

fragment_avaliacao.xml:

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
tools:context=".ui.secoes.AvaliacaoFragment">

<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/container_titulo_avaliacao"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            >

            <TextView
                android:id="@+id/nome_avaliacao"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="@string/padrao_avaliacao_sem_nome"
                android:textSize="24sp"
                android:textColor="@color/colorIcons"
                android:textAlignment="center"
                />

            <TextView
                android:id="@+id/data_avaliacao"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="16dp"
                android:text="@string/padrao_formato_data_hora"
                android:textSize="14sp"
                android:textColor="@color/colorPrimaryLight"
                android:textAlignment="center"
                />

        </LinearLayout>

        <TextView
            android:id="@+id/texto_sem_secoes"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@+id/container_titulo_avaliacao"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="8dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="8dp"
            android:textAlignment="center"
            android:textSize="16sp"
            android:text="@string/nenhuma_secao_criada"
            android:textColor="@color/colorPrimaryLight"
            />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/secaoRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@+id/container_titulo_avaliacao"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout></androidx.constraintlayout.widget.ConstraintLayout>

来自 SecaoAdapter 的 secaoList 的 Logcat 循环(正确循环到所有元素):

我创建了一个 gif 来显示 recyclerView 上的元素发生了什么:

在这里您可以看到它显示第七个元素,然后第八个被第九个替换,随后被第十个替换。最后两个元素(应该是第九个和第十个)由第一个和第二个列表元素构成。

提前感谢您的帮助。

【问题讨论】:

  • 从数据库中获取数据时,它们是否排序?还是你拿到后排序?
  • 因为在SecaoAdapter的setData方法中,所有从DB获取的列表都替换为之前的列表,然后调用dataSetChanged()。应该保证列表(适配器中的列表和新获取的列表)在某些位置有相同的项目。
  • @com_run,这不会导致这种情况。 notifyDataSetChanged() 表示完全刷新并假设什么都没有,甚至大小都是一样的。
  • 这是一个笨蛋。我在您的代码中找不到任何错误。您还可以在setData 函数中记录一些内容,以查看它是否在您滚动时被调用?当视图已经在屏幕上并且您没有使用稳定的 ID 做任何事情时,这些值正在发生变化,这很奇怪。
  • 最好检查两个列表并在 setData() 方法中设置日志。

标签: android android-studio kotlin android-recyclerview


【解决方案1】:

所以这里的错误是您在适配器中只引用了一个被覆盖的绑定。每次调用 onCreateViewHolder 时,您都在更改 binding 引用。这看起来不错的原因是onCreateViewHolder 调用之后是onBindViewHolder 调用屏幕上可见的项目。但是,当您滚动时,只需调用 onBindViewHolder 以重新绑定回收的视图。

您应该做的是使用您的ViewHolder 来存储各个绑定,然后使用holder.binding 之类的东西在onBindViewHolder 中获取引用。

我建议您阅读视图持有者模式以及如何实现它!

【讨论】:

  • 感谢@HenryTwist。了解。我根据您的回答做了什么:在SecaoViewHolder 类中插入代码val binding = itemBinding,然后在onBindViewHolder,将字段调用从binding.fieldName 更改为holder.binding.fieldName,并且知道工作正常。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
相关资源
最近更新 更多