过滤 RecyclerView 后位置变得混乱

Positions getting messed up after filtering RecyclerView

所以这是我的问题:

(我展示的是房子)

HouseAdapter(recyclerview 适配器):

class HouseAdapter(private var recyclerViewInterface: RecyclerViewInterface) : RecyclerView.Adapter<HouseAdapter.HouseViewHolder>() {

    inner class HouseViewHolder(val binding: ItemHouseBinding) : RecyclerView.ViewHolder(binding.root){

        private val formatter : NumberFormat = DecimalFormat("$#,###")


        fun bind(house: House) {
            binding.tvPrice.text = formatter.format(house.price)
            "${house.zip} ${house.city}".also { binding.tvAddress.text = it }
            binding.tvBed.text = house.bedrooms.toString()
            binding.tvBath.text = house.bathrooms.toString()
            binding.tvLayers.text = house.size.toString()

            val houseImageUrl : String = BASE_URL + house.image
            Glide.with(binding.root.context).load(houseImageUrl).centerCrop().into(binding.ivHouse)
        }


    }

    private val diffCallBack = object : DiffUtil.ItemCallback<House>(){

        // Call to check whether two items represent the same item
        override fun areItemsTheSame(oldItem: House, newItem: House): Boolean {
            return oldItem.id == newItem.id
        }

        // Call to check whether two items have the same data
        override fun areContentsTheSame(oldItem: House, newItem: House): Boolean {
            return oldItem == newItem
        }

    }

    private val differ = AsyncListDiffer(this, diffCallBack)

    var houses: List<House>
        get() = differ.currentList
        set(value) {differ.submitList(value)}


    override fun getItemCount(): Int {
        return houses.size
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HouseViewHolder {
        return HouseViewHolder(ItemHouseBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        ))
    }


    override fun onBindViewHolder(holder: HouseViewHolder, position: Int) {
        println("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO : $position")

        holder.bind(houses[position])

        holder.binding.vBody.setOnClickListener {
            recyclerViewInterface.onItemClick(position)
        }
    }


    fun setFilteredList(filteredList: List<House>){
        this.houses = filteredList

    }

}

HomeFragment(包含回收站视图):

class HomeFragment : Fragment(), RecyclerViewInterface {

    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!

    private lateinit var container : ViewGroup

    private lateinit var houseAdapter: HouseAdapter
    private lateinit var listHouses: ArrayList<House>
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentHomeBinding.inflate(inflater, container, false)
        if (container != null) {
            this.container = container
        }

        setupRecyclerView()

        lifecycleScope.launchWhenCreated {
            val response = try {
                RetrofitInstance.api.getHouses(API_KEY)
            } catch (e: IOException) {
                Log.e(TAG, "IOException, you might not have internet connection")
                return@launchWhenCreated
            } catch (e: HttpException){
                Log.e(TAG, "HttpException, unexpected response ")
                return@launchWhenCreated
            }
            if (response.isSuccessful && response.body() != null){
                listHouses = (response.body() as ArrayList<House>?)!!

                houseAdapter.houses = listHouses

                listHouses.sortWith(compareBy {it.price})
                listHouses.forEach {println(it.price) }


            }else{
                Log.e(TAG, "Response not successful")
            }
        }

        setupSearchView()

        return binding.root
    }
    // Called on fragment launch - Add OnQueryTextListener to the SearchView
    private fun setupSearchView(){
        binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener,
            androidx.appcompat.widget.SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String?): Boolean {
                return false
            }

            override fun onQueryTextChange(newText: String): Boolean {
                filterList(newText)
                return true
            }

        })
    }

    // Called when text from SearchView is changed - Create new filtered list or display empty information
    private fun filterList(text: String) {
        val filteredList = arrayListOf<House>()

        listHouses.forEach { house : House ->
            if (house.city.contains(text) || house.zip.contains(text)){
                filteredList.add(house)
            }
        }

        if (filteredList.isEmpty()){
            binding.rvHouses.visibility = View.INVISIBLE
            binding.ivSearchEmpty.visibility = View.VISIBLE
            binding.tvSearchEmpty1.visibility = View.VISIBLE
            binding.tvSearchEmpty2.visibility = View.VISIBLE
        }else{
            houseAdapter.setFilteredList(filteredList)
            binding.rvHouses.visibility = View.VISIBLE
            binding.ivSearchEmpty.visibility = View.INVISIBLE
            binding.tvSearchEmpty1.visibility = View.INVISIBLE
            binding.tvSearchEmpty2.visibility = View.INVISIBLE
        }

    }

    // Called on fragment launch - Setup the RecyclerView with the HouseAdapter
    private fun setupRecyclerView() = binding.rvHouses.apply {
        houseAdapter = HouseAdapter(this@HomeFragment)
        adapter = houseAdapter
        layoutManager = LinearLayoutManager(context)
    }


    // Called when user click on House item - Pass properties from the chosen house to DetailActivity
    override fun onItemClick(position: Int) {
        val i = Intent(this.context, DetailActivity::class.java)
        println("OOOOOOOOOOOOOOOOOOOOOOOOOOOOO : $position")
        i.putExtra("PRICE", listHouses[position].price)
        i.putExtra("IMAGE_URL", listHouses[position].image)
        i.putExtra("BEDS", listHouses[position].bedrooms)
        i.putExtra("BATH", listHouses[position].bathrooms)
        i.putExtra("SIZE", listHouses[position].size)
        i.putExtra("LAT", listHouses[position].latitude)
        i.putExtra("LONG", listHouses[position].longitude)
        i.putExtra("DESC", listHouses[position].description)
        startActivity(i)
    }

}

(我使用 println("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO : $position") 跟踪项目在 recylcerview 的 bindviewholder 函数中的位置,当我单击一个项目以查看它是否对应时...答案是: “这取决于”有时是有时不是。我试图找到一些逻辑,但我不确定我的理论:我认为当我过滤 recyclerview 之前没有看到所有项目时,过滤列表不知道什么位置给那些“看不见”的物品。

如果您除了我的问题还有其他建议,我很乐意听取(正如我所说,我目前正在学习 kotlin)

查看 positions in RecyclerView

有布局和适配器两种。你想使用适配器之一。因此,将 onBindViewHolder 函数更改为使用 getAbsoluteAdapterPosition:

holder.binding.vBody.setOnClickListener {
    recyclerViewInterface.onItemClick(holder.absoluteAdapterPosition) // not position
}