I am a newcomer in Kotlin and synchronous programming. I have code:
open class MyFragment : DialogFragment() {
private var fragmentBinding: FragmentBinding? = null
private var resultList: List<MyObject> = ArrayList()
private var list = ArrayList<MyObjectItem>()
private lateinit var adapter: MynAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
resultList = getActivity.loadObjects()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentBinding = FragmentBinding.inflate(inflater, container, false)
lifecycleScope.launch {
getListData()
initAdapter()
}
return fragmentBinding?.root
}
private fun initAdapter() {
val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(requireContext())
fragmentBinding?.recyclerView?.layoutManager = layoutManager
adapter = MyAdapter(list, requireContext(), this, this)
fragmentBinding?.recyclerView?.adapter = adapter
}
private fun getListData() {
for (value in resultList) {
list.add(
MyObjectItem(
value.title!!,
value.numbert!!,
)
)
}
}
as result I get empty list in UI. But in debug I see, that from loadObjects()
method I get not empty list. I understand , that it works in debug mode only because I stop execution of UI thread on my breakpoint and I should set up the view (namely calling initAdapter()
on the UI thread, not in my worker thread. But I don't understand, how can I do this....
CodePudding user response:
Maybe your issue is different, but if you are going to load data asynchronous you need to use at least RecyclerView.Adapter#notifyDataSetChanged() (You can get better results using notifyItemChanged methods but those are more advanced).
Your code should look similar to:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentBinding = FragmentBinding.inflate(inflater, container, false)
initAdapter()
lifecycleScope.launch {
adapter.list = getListData()
adapter.notifyDataSetChanged() // Notify changes in the adapter
}
return fragmentBinding?.root
}
CodePudding user response:
Here is the simple example of the adapter. You can use addAll() function of the adapter to add the data into the adapter.
public class SenListAdapter() :
RecyclerView.Adapter<SenListAdapter.MyViewHolder>() {
var data: MutableList<QuizResponse.Datum> = ArrayList()
var itemListener: EventListener? = null
var inflater: LayoutInflater? = null
private var onl oadMoreListener: onl oadMoreListener? = null
var mData = ""
var context:Context?=null
constructor(context: Context, quizType: String) : this() {
this.quizType = quizType
this.context=context
}
override fun getItemViewType(position: Int): Int {
return position
}
fun addAll(mData: List<QuizResponse.Datum>?) {
data.clear();
data.addAll(mData!!)
notifyDataSetChanged()
}
fun clear() {
data.clear()
notifyDataSetChanged()
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): SenListAdapter.MyViewHolder {
inflater = LayoutInflater.from(parent.context)
val itemHomeSenBinding = DataBindingUtil.inflate<ItemHomeSenBinding>(
inflater!!,
R.layout.item_home_sen, parent, false
)
return MyViewHolder(itemHomeSenBinding)
}
override fun onBindViewHolder(holder: SenListAdapter.MyViewHolder, position: Int) {
populateItemRows(holder, position)
}
private fun populateItemRows(holder: SenListAdapter.MyViewHolder, position: Int) {
var item = data.get(position)
holder.itemHomeSenBinding.tvQuizName.text = item.name
holder.itemHomeSenBinding.tvCost.text = "₹ " item.entryFee.toString()
holder.itemHomeSenBinding.tvEstimatedTime.text = item.time
}
override fun getItemCount(): Int {
return data.size
}
inner class MyViewHolder(var itemHomeSenBinding: ItemHomeSenBinding) :
RecyclerView.ViewHolder(
itemHomeSenBinding.root
) {
init {
setEventlistener(itemListener)
}
}
interface EventListener {
fun onClick(position: Int, item: QuizResponse.Datum?)
}
fun setEventlistener(onItemClick: EventListener?) {
itemListener = onItemClick
}
}
To set the adapter just use the object of recycler view :
var adapter=SenListAdapter()
adapter.addAll(<Your List>)
recyclerView.setAdapter(adapter)
CodePudding user response:
resultList = getActivity.loadObjects()
- I suppose this is your suspend (network) call. This shouldn't be called from Main thread, move this call in coroutine launcher, and when is done you can init your adapter from Main thread.
Try this:
open class MyFragment : DialogFragment() {
private var fragmentBinding: FragmentBinding? = null
private var list = ArrayList<MyObjectItem>()
private lateinit var adapter: MynAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentBinding = FragmentBinding.inflate(inflater, container, false)
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO){
val res = getActivity.loadObjects()
withContext(Dispatchers.Main){
getListData(res)
initAdapter()
}
}
return fragmentBinding?.root
}
private fun initAdapter() {
val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(requireContext())
fragmentBinding?.recyclerView?.layoutManager = layoutManager
adapter = MyAdapter(list, requireContext(), this, this)
fragmentBinding?.recyclerView?.adapter = adapter
}
private fun getListData(res:List<MyObject>) {
for (value in res) {
list.add(
MyObjectItem(
value.title!!,
value.numbert!!,
)
)
}
}