【问题标题】:Positions getting messed up after filtering RecyclerView过滤 RecyclerView 后位置变得混乱
【发布时间】:2022-11-30 17:24:55
【问题描述】:

所以这是我的问题:

  • 我正在从 API 获取数据(成功)
  • 我在回收站视图中显示此数据(成功)
  • 我正在使用搜索视图过滤回收视图(成功)
  • 我在 recyclerview 中的项目上有一个点击侦听器,它正在使用项目 [位置] 信息开始一个新的活动,并且在我过滤我的回收站视图之前这个工作完美。当我这样做时,过滤器,我的项目的位置变得混乱,所以我给新活动的信息是错误的......我起初以为这是因为我使用了 List 而不是 ArrayList(可变的) 但问题似乎也不是来自那里...... 事实是我被困住了,我正在学习 android 和 kotlin,这就是我在没有帮助的情况下能走多远:

(我展示的物品是房子)

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 之前没有看到所有项目时,过滤列表不知道什么位置给那些“看不见”。

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

【问题讨论】:

    标签: android kotlin android-recyclerview searchview


    【解决方案1】:

    查看positions in RecyclerView

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

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

    【讨论】:

      【解决方案2】:

      你必须改变你的 onBindViewHolder

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

      只需用“adapterPosition”改变“位置”

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-24
        • 1970-01-01
        • 2021-12-31
        相关资源
        最近更新 更多