[英]How to observe a list from Adapter in Android [Kotlin]
In my case I am trying to get a list of String from my Adapter and use it in my Fragment but upon debugging using Logs I found that the list is getting updated inside the onBindViewHolder
but not outside it.在我的情况下,我试图从我的适配器中获取一个字符串列表并在我的片段中使用它,但是在使用日志进行调试时,我发现该列表在onBindViewHolder
内部而不是在它之外得到更新。 So when I try to access the list from my Fragment I am getting an empty list of String.所以当我尝试从我的片段访问列表时,我得到一个空的字符串列表。
I have spent few hours trying to figure this but can't find a feasible solution.我花了几个小时试图解决这个问题,但找不到可行的解决方案。
My Approach: I am thinking of an approach to save this list in a room table and then query it back in the Fragment.我的方法:我正在考虑一种方法将此列表保存在房间表中,然后在片段中查询它。 Though it may solve the issue but is it the only way?虽然它可以解决问题,但它是唯一的方法吗? Are there any other ways to achieve this result?有没有其他方法可以达到这个结果?
My Adapter我的适配器
class FloorProfileDialogAdapter() : RecyclerView.Adapter<FloorProfileDialogAdapter.MyViewHolder>() {
var floors = emptyList<String>()
inner class MyViewHolder(val binding: ScheduleFloorDialogItemBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ScheduleFloorDialogItemBinding.inflate(inflater, parent, false)
return MyViewHolder(binding)
}
private val checkedFloors: MutableList<String> = mutableListOf()
//List of uniquely selected checkbox to be observed from New Schedule Floor Fragment
var unique: List<String> = mutableListOf()
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentFloor = floors[position]
Timber.d("Current floor: $currentFloor")
holder.binding.floorCheckBox.text = "Floor $currentFloor"
//Checks the checked boxes and updates the list
holder.binding.floorCheckBox.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isChecked) {
Timber.d("${buttonView.text} checked")
checkedFloors.add(buttonView.text.toString())
} else if (!buttonView.isChecked) {
Timber.d("${buttonView.text} unchecked")
checkedFloors.remove(buttonView.text)
}
unique = checkedFloors.distinct().sorted()
Timber.d("List: $unique")
}
}
fun returnList(): List<String> {
Timber.d("$unique")
return unique
}
override fun getItemCount(): Int {
return floors.size
}
@SuppressLint("NotifyDataSetChanged")
fun getAllFloors(floorsReceived: List<String>) {
Timber.d("Floors received : $floorsReceived")
this.floors = floorsReceived
notifyDataSetChanged()
}
}
Fragment code where I am trying to read it我试图阅读的片段代码
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Chosen Floors
val chosenFloors = floorProfileDialogAdapter.returnList()
Timber.d("Chosen floors : $chosenFloors")
}
Note: The list I am trying to receive is var unique: List<String> = mutableListOf
.注意:我试图接收的列表是var unique: List<String> = mutableListOf
。 I tried to get it using the returnList()
but the log in that function shows that list is empty.我尝试使用returnList()
获取它,但 function 的日志显示该列表为空。 Similarly the Log in fragment shows that it received an empty list.同样,登录片段显示它收到了一个空列表。
Edit 1: Class to fill the Adapter Floors using getAllFloors()
编辑 1:Class 使用getAllFloors()
填充适配器楼层
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val floorList: MutableList<String> = mutableListOf()
var profileName: String? = ""
profileName = args.profileName
//Profile name received
Timber.d("Profile name : $profileName")
//Getting list of all floors
createProfileViewModel.totalFloors.observe(viewLifecycleOwner) {
Timber.d("List of floors received : $it")
val intList = it.map(String::toInt)
val maxFloorValue = intList.last()
var count = 0
try {
while (count <= maxFloorValue) {
floorList.add(count.toString())
count++
}
} catch (e: Exception) {
Timber.d("Exception: $e")
}
floorProfileDialogAdapter.getAllFloors(floorList)
Timber.d("Floor List : $floorList")
}
When you first set up your Fragment and create your Adapter, unique
is empty:当您第一次设置 Fragment 并创建 Adapter 时, unique
为空:
var unique: List<String> = mutableListOf()
(if you have some checked state you want to save and restore, you'll have to initialise this with your checked data) (如果您有一些检查过的 state 想要保存和恢复,则必须使用检查过的数据对其进行初始化)
In onViewCreated
, during Fragment setup, you get a reference to this (empty) list:在onViewCreated
中,在 Fragment 设置期间,您会获得对此(空)列表的引用:
// Fragment onViewCreated
val chosenFloors = floorProfileDialogAdapter.returnList()
// Adapter
fun returnList(): List<String> {
return unique
}
So chosenFloors
is a reference to this initial entry list.所以chosenFloors
是对这个初始条目列表的引用。 But when you actually update unique
in onBindViewHolder
但是当你真正更新onBindViewHolder
中的unique
unique = checkedFloors.distinct().sorted()
you're replacing the current list with a new list object.您正在用新列表 object 替换当前列表。 You're not updating the existing list (even though you made it a MutableList
).您没有更新现有列表(即使您将其MutableList
)。 So you never actually add anything to that empty list you started with, and chosenFloors
is left pointing at a list that contains nothing, while the Adapter has discarded it and unique
holds a completely different object.因此,您实际上从未向您开始的那个空列表添加任何内容,并且chosenFloors
指向一个不包含任何内容的列表,而适配器已丢弃它并且unique
拥有完全不同的 object。
The solution there is to make unique
a val
(so you can't replace it) and just change its contents, eg解决方案是制作unique
的val
(因此您无法替换它)并仅更改其内容,例如
unique.clear()
unique += checkedFloors.distinct().sorted()
But I don't feel like that's your problem.但我不觉得那是你的问题。 Like I pointed out, that list is initially empty anyway, and you're grabbing it in your Fragment during initialisation just so you can print out its contents, as though you expect it to contain something at that point.就像我指出的那样,该列表最初是空的,并且您在初始化期间将其抓取到 Fragment 中,以便您可以打印出它的内容,就好像您希望它此时包含某些内容一样。 Unless you initialise it with some values, it's gonna be empty.除非你用一些值初始化它,否则它将是空的。
If you're not already storing/restoring them, you'll need to handle that!如果您还没有存储/恢复它们,则需要处理它! I posted some code to do that on another answer so I'll just link that instead of repeating myself.我在另一个答案上发布了一些代码来做到这一点,所以我只是链接它而不是重复自己。 That code is storing indices though, not text labels like you're doing.不过,该代码正在存储索引,而不是像您正在做的文本标签。 Indices are much cleaner and avoid errors - the text is more of a display thing, a property of the item the specific (and unique) index refers to.索引更清晰并避免错误 - 文本更多的是显示内容,特定(和唯一)索引所指的项目的属性。 (But you can store a string array in SharedPreferences
if you really want to.) (但如果你真的想的话,你可以在SharedPreferences
中存储一个字符串数组。)
Also you're not actually updating your ViewHolder
to display the checked state for the current item in onBindViewHolder
.此外,您实际上并没有更新ViewHolder
以显示 onBindViewHolder 中当前项目的选中onBindViewHolder
。 So whatever ViewHolder
you happen to have been given (there's only a few of them for the list, they get reused) it's just showing whatever its checkbox was last set to, by you poking at it.因此,无论您碰巧获得了什么ViewHolder
(列表中只有少数几个,它们都会被重用),它只是显示它的复选框最后设置的任何内容,通过您戳它。 Check an item, then scroll the list and see what happens!检查一个项目,然后滚动列表,看看会发生什么!
So you need to check or uncheck the box so it's correct for the item you're displaying.因此,您需要选中或取消选中该框,以便它对您正在显示的项目是正确的。 This is pretty easy if you're storing the checked items by indices:如果您按索引存储检查的项目,这非常容易:
// explicitly set the checked state, depending on whether the item at 'position' is checked
holder.binding.floorCheckBox.checked = checkedItems[position]
You can work out something similar for your text label approach, but again I wouldn't recommend doing things that way.您可以为您的文本 label 方法制定类似的方法,但我不建议这样做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.