【问题标题】:Recycler View with duplicate rows after refreshing activity刷新活动后具有重复行的回收站视图
【发布时间】:2022-01-19 22:51:27
【问题描述】:

做了一些研究后,我发现每次刷新活动时我都需要清除我正在使用的列表,在尝试刷新不同位置的列表后,结果保持不变,我决定给我的大脑休息了一下,寻求帮助。刷新发生在 PostDialogFragment 类上

我已经圈出了导致问题的回收站视图

后适配器

package com.example.osrscritic.view

import android.text.method.ScrollingMovementMethod
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.osrscritic.databinding.PostRowItemBinding
import com.example.osrscritic.model.Skillvalue
import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.QueryDocumentSnapshot
import com.google.firebase.firestore.QuerySnapshot

class PostsAdapter: RecyclerView.Adapter<PostsViewHolder>() {

    val postsMutableList : MutableList<DocumentSnapshot> = mutableListOf()

    fun setPostsList(statsList: List<DocumentSnapshot>) {
        postsMutableList.addAll(statsList)
        notifyDataSetChanged()
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostsViewHolder {

        val binding = PostRowItemBinding.inflate(LayoutInflater.from(parent.context)
            ,parent,false)

        return PostsViewHolder(binding)
    }

    override fun onBindViewHolder(holder: PostsViewHolder, position: Int) {

        val post = postsMutableList[position]
        holder.binding.tvCritic.text = post["critic"].toString()

        val posts = post["posts"] as List<String>

        var count = 0;

        holder.binding.tvActualPost.movementMethod = ScrollingMovementMethod()

        for(p in posts) {
            holder.binding.tvActualPost.append("${count}: " + p + "\n")
            count++
        }



    }
    
    override fun getItemCount() = postsMutableList.size
}

class PostsViewHolder(val binding: PostRowItemBinding) : RecyclerView.ViewHolder(binding.root)

DisplayUserActivty

package com.example.osrscritic

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.osrscritic.databinding.ActivityDisplayUserBinding
import com.example.osrscritic.view.PostDialogFragment
import com.example.osrscritic.view.PostsAdapter
import com.example.osrscritic.view.StatsAdapter
import com.example.osrscritic.viewmodel.DisplayUserViewModel
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.DocumentSnapshot
import org.koin.androidx.viewmodel.ext.android.viewModel

class DisplayUserActivity : AppCompatActivity() {

    private val displayUserViewModel : DisplayUserViewModel by viewModel()
    lateinit var binding: ActivityDisplayUserBinding
    val statsAdapter = StatsAdapter()
    val postsAdapter = PostsAdapter()
    lateinit var critiquing: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val extras = intent.extras


        critiquing = extras?.getString("c")!!
        binding = ActivityDisplayUserBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.rsStatsRv.adapter = statsAdapter
        binding.rsStatsRv.layoutManager = LinearLayoutManager(this,
            LinearLayoutManager.VERTICAL, false)

        binding.rvPosts.adapter = postsAdapter
        binding.rvPosts.layoutManager = LinearLayoutManager(this,
            LinearLayoutManager.VERTICAL, false)

        configureObservers()

        displayUserViewModel.getFirebaseRef()

        displayUserViewModel.getOSRSPlayer()

        binding.ivWritePost.setOnClickListener {

           PostDialogFragment.newInstance(critiquing).show(supportFragmentManager
               , PostDialogFragment.KEY)


        }

    }
    
    private fun configureObservers() {



        displayUserViewModel.displayUserLiveData.observe(this, {
            statsAdapter.setStatsList(it.skillvalues)
            binding.tvRunescapeName.text = String.format("Account Name: %s", it.name)
            binding.tvCombat.text = String.format("Combat Level: %s", it.combatlevel.toString())

        })

        displayUserViewModel.loadingState.observe(this, {
            when(it){
                true -> binding.pgBar.visibility = View.VISIBLE
                false -> binding.pgBar.visibility = View.GONE
            }
        })

        displayUserViewModel.errorData.observe(this, {
            Toast.makeText(this, it, Toast.LENGTH_LONG).show()

        })

        displayUserViewModel.displayUserPostsLiveData.observe(this, {
            postsAdapter.postsMutableList.clear()
            it.document(critiquing).collection("posts").get()
                .addOnSuccessListener {
                    postsAdapter.setPostsList(it.documents)
                }
            
        })
        
    }

}

PostDialogFragment

package com.example.osrscritic.view

import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import androidx.fragment.app.DialogFragment
import android.widget.LinearLayout

import android.widget.TextView

import android.widget.EditText

import android.view.Gravity
import androidx.core.app.ActivityCompat.recreate
import com.example.osrscritic.DisplayUserActivity
import com.example.osrscritic.viewmodel.DisplayUserViewModel
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FieldPath
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.SetOptions
import org.koin.androidx.viewmodel.ext.android.viewModel


class PostDialogFragment: DialogFragment() {

    private val displayUserViewModel : DisplayUserViewModel by viewModel()
    val currentUser = FirebaseAuth.getInstance().currentUser
    lateinit var critiquing: String
    val postsAdapter = PostsAdapter()


    companion object {
        const val KEY: String = "KEY2"


        //lateinit var param: String

        fun newInstance(text: String): PostDialogFragment {
            val args = Bundle()
            val postDialogFragment = PostDialogFragment()
            args.putString(KEY, text)
            postDialogFragment.arguments = args
            return postDialogFragment

        }

    }


    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {


        critiquing = arguments?.getString(KEY)!!

        Log.d("*******8", critiquing)

        val builder = AlertDialog.Builder(activity)

        val layout = LinearLayout(activity)
        val parms = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        )
        layout.orientation = LinearLayout.VERTICAL
        layout.layoutParams = parms

        layout.gravity = Gravity.CLIP_VERTICAL
        layout.setPadding(2, 2, 2, 2)

        val tv = TextView(activity)
        tv.text = "Text View title"
        tv.setPadding(40, 40, 40, 40)
        tv.gravity = Gravity.CENTER
        tv.textSize = 20f

        val et = EditText(activity)
        var etString = ""

        et.addTextChangedListener(object: TextWatcher{
            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

            override fun afterTextChanged(p0: Editable?) {

                Log.d("etText", et.text.toString())

                etString = et.text.toString()
            }

        })
        val tv1 = TextView(activity)
        tv1.text = "Input Student ID"

        val tv1Params = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        )
        tv1Params.bottomMargin = 5
        //layout.addView(tv1, tv1Params)
        layout.addView(
            et,
            LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            )
        )
        //param = arguments?.getString(KEY)!!
        builder.setTitle("New Post")
        builder.setView(layout).setPositiveButton("Done", object: DialogInterface.OnClickListener{
            override fun onClick(p0: DialogInterface?, p1: Int) {

                activity?.let {
                    displayUserViewModel.displayUserPostsLiveData.observe(it, {
                        val post : MutableMap<String, Any> = mutableMapOf()

                        val posts : MutableList<String> = mutableListOf()

                        posts.add(etString)

                        post["critic"] = currentUser?.email!!
                        post["posts"] = posts

                        if(it.document(critiquing).collection("posts").document().equals(null)) {

                            it.document(critiquing).collection("posts")
                                .document(currentUser?.email!!).set(post)
                        }


                        it.document(critiquing).collection("posts")
                            .document(currentUser?.email!!).update("posts", FieldValue.arrayUnion(etString))

                    })

                }

                activity!!.finish()
                activity!!.overridePendingTransition( 0, 0);
                startActivity(activity!!.intent)
                  //trying to clear the list here once being refreshed
                postsAdapter.postsMutableList.clear()
                activity!!.overridePendingTransition( 0, 0);

            }

        })

        builder.setNegativeButton("Cancel", object:DialogInterface.OnClickListener{
            override fun onClick(p0: DialogInterface?, p1: Int) {
                dismiss()
            }

        })

        return builder.create()
    }

}

【问题讨论】:

    标签: android android-recyclerview android-dialogfragment


    【解决方案1】:

    覆盖这些通常可以解决此类问题:

    • getItemId:返回唯一标识每个项目的long。例如,您可以为每个新项目对象使用自动递增变量id,或使用一些hashcode。这应该与setHasStableIds(true) 配对。 (默认返回RecyclerView.NO_ID,其值为-1)。

    这有助于确保RecyclerView 例如正确识别项目,知道何时必须调用 OnBindViewHolder 或在 getAdapterPosition() 上返回精确值。

    • getItemCount:返回带有当前项目数的int。通常是数据所在的Array.size()。 (默认返回0)。
    • getItemType:如果有多种视图类型,则为每种类型的ViewHolder返回一个唯一编号(通常以0开头)。 (默认返回0)。

    【讨论】:

    • 谢谢,这是清除我使用的列表和覆盖 getItemId 和 getItemViewType 以及 setHasStableIds 为 true 的组合
    • 很高兴您发现它有帮助!我添加了更多信息以使答案更详细。
    猜你喜欢
    • 2016-08-21
    • 1970-01-01
    • 1970-01-01
    • 2019-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多