Home > front end >  What should I do? The RecyclerView I used on a fragment in a TabLayout fragment won't show
What should I do? The RecyclerView I used on a fragment in a TabLayout fragment won't show

Time:09-27

I am trying to show a RecyclerView on a fragment in a TabLayout that is also a fragment. However, the RecyclerView still refuses to show up even if there is no errors. Based on information I have gathered, it should be possible for a fragment of a TabLayout to have a RecyclerView. However, I don't know if the problem lies within the fact that I built my TabLayout on a fragment causing the recyclerview to not show. I figure I must be doing something wrong and I need some help. Here is my code:

TabLayoutFragment

    var viewPager = root.findViewById(R.id.viewPager) as ViewPager
    var tablayout = root.findViewById(R.id.tabLayout) as TabLayout

    val fragmentAdapter = FragmentAdapter(requireActivity().supportFragmentManager)
    fragmentAdapter.addFragment(OpenLoansFragment(), "Active")
    fragmentAdapter.addFragment(ClosedLoansFragment(), "Closed")

    viewPager.adapter = fragmentAdapter
    tablayout.setupWithViewPager(viewPager)

RecyclerView Fragment

private lateinit var swipeRefreshLayout:SwipeRefreshLayout
private lateinit var loanRecRcv:RecyclerView
private var list: ArrayList<LoanRecordModel> = ArrayList()
private var loanRecRcvAdapter = LoanRecRcvAdapter(list, this)

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    _binding = FragmentOpenLoansBinding.inflate(inflater, container, false)
    val root: View = binding.root

    return root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    list = arrayListOf(
        LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"),
        LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"),
        LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"),
        LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"),
        LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open")

    )

    initView()

    loanRecRcv.apply{
        layoutManager = LinearLayoutManager(activity)
        adapter = loanRecRcvAdapter
    }
}

private fun initView() {

    swipeRefreshLayout = requireActivity().findViewById(R.id.swipeRefresh)
    loanRecRcv = requireActivity().findViewById(R.id.loanRecRcv)

}

CodePudding user response:

You're displaying an empty list in your RecyclerView. First you create an empty adapter at construction time:

// empty list
private var list: ArrayList<LoanRecordModel> = ArrayList()
// adapter created with the empty list
private var loanRecRcvAdapter = LoanRecRcvAdapter(list, this)

And later you apply that adapter to your RV:

loanRecRcv.apply{
    ...
    adapter = loanRecRcvAdapter
}

You're creating a list of items in onViewCreated

list = arrayListOf(
    LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"),
    ...
)

but that's replacing the list stored in your list variable. You already created your Adapter earlier (at construction) using the original list, so it's still using that empty list internally. It's not referencing your list variable.


Two basic options - either make list a MutableList and update its contents instead of replacing it:

// empty mutable list - you never need to actually use ArrayLists or whatever
// made it a val so you can't replace it!
private val list = mutableListOf<LoanRecordModel>()
// pass that list object to the adapter, so it's shared
private var loanRecRcvAdapter = LoanRecRcvAdapter(list, this)

...

// update your list contents
list.addAll(listOf(
    LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"),
    ...
))
// notify the adapter that it needs to refresh since its data set (the shared list) has changed
adapter.notifyDataSetChanged()

or (better) just pass new data to the adapter and let it store that internally, handle updating itself etc:

// in your adapter

// store the current data here - initially an empty list, so it can still display
private var list: List<LoanRecordModel> = emptyList()

fun setData(data: List<LoanRecordModel>) {
    // calling toList makes a new list object - even if the 'data' list object gets
    // modified later, we have our own internal list object that won't be affected
    list = data.toList()
    // update the display however you like
    notifyDataSetChanged()
}

// in your fragment

adapter.setData(listOf(
    LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"),
    ...
))

This way's better because the adapter's data set is held internally, instead of in your fragment. You have a simple setData function that takes the new data, and handles updating the adapter as required. The caller doesn't need to know about any of this, it just passes in data. You can remove your List parameter from the Adapter's constructor too - you weren't initialising it to anything but an empty list anyway - and now that's the default value for that internal list property


btw, to initialise that list of identical things, you could do

// create an immutable list
val list = List(5) { LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open") }

// or add to a mutable list
list.addAll(List(5) { LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open") })

// or another way
repeat(5) {
    list.add(LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open"))
}

if you like

  • Related