Kevin Cianfarini
Kevin Cianfarini

Reputation: 699

RecyclerView within a ViewPager unpopulated despite Adapter containing data

I'm currently using a ViewPager and TabLayout for a couple of similar fragments. The fragments hosted within the ViewPager all have a RecyclerView. When I swipe more than one page over, the Fragment (I think?) is destroyed. When I swipe back it's recreated.

Upon debugging, I found that the adapter within the Fragment is non null and populated with the data from before. However, once the fragment is visible it no longer displays any entries.

Here's a video of what's going on. enter image description here

This is a fragment within the ViewPager

class ArtistListFragment : Fragment(), ListView {

    override val title: String
        get() = "Artists"

    private val artistList: RecyclerView by lazy { artist_list }
    @Inject lateinit var api: SaddleAPIManager

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
            = container?.inflate(R.layout.fragment_list_artist)

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

        artistList.apply {
            setHasFixedSize(true)
            if (adapter == null) {
                adapter = ViewTypeAdapter(mapOf(AdapterConstants.ARTIST to ArtistDelegateAdapter()))
            }
            layoutManager = LinearLayoutManager(context)
            if (savedInstanceState != null && savedInstanceState.containsKey("artist")) {
                (adapter as ViewTypeAdapter).setItems(savedInstanceState.getParcelableArrayList<Artist>("artists"))
            }
        }

        (activity?.application as SaddleApplication).apiComponent.inject(this)
        refreshData()
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putParcelableArrayList(
                "artists",
                (artistList.adapter as ViewTypeAdapter).items as ArrayList<out Parcelable>
        )
    }

    private fun refreshData() = launch(UI) {
        val result = withContext(CommonPool) { api.getArtists() }
        when(result) {
            is Success -> (artistList.adapter as ViewTypeAdapter).setItems(result.data.results)
            is Failure -> Snackbar.make(artistList, result.error.message ?: "Error", Snackbar.LENGTH_LONG).show()
        }
    }

}

This is the Fragment hosting the ViewPager

class NavigationFragment : Fragment() {

    private val viewPager: ViewPager by lazy { pager }
    private val tabLayout: TabLayout by lazy { tab_layout }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
            = container?.inflate(R.layout.fragment_navigation)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewPager.apply {
            adapter = NavigationPageAdapter(childFragmentManager)
        }

        tabLayout.apply {
            setupWithViewPager(viewPager)
        }
    }
}

The adapter I'm using for paging

class NavigationPageAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) {

    companion object {
        const val NUM_PAGES = 4
    }

    private val pages: List<Fragment> = (0 until NUM_PAGES).map {
        when (it) {
            0 -> ArtistListFragment()
            1 -> AlbumListFragment()
            2 -> TrackListFragment()
            else -> PlaylistListFragment()
        } as Fragment
    }

    override fun getItem(position: Int): Fragment = pages[position]

    override fun getCount(): Int = NUM_PAGES

    override fun getPageTitle(position: Int): CharSequence? = (pages[position] as ListView).title
}

I've tried overriding onSaveInstanceState and reading the information from the bundle. It doesn't seem to do anything. The problem seems to actually be the RecyclerView displaying? It's populated with data which is why I'm stumped.

Upvotes: 0

Views: 573

Answers (2)

Kevin Cianfarini
Kevin Cianfarini

Reputation: 699

I've figured out the problem. While setOffScreenPageLimit(NavigationAdapter.NUM_PAGES) did work, I am cautious to use it because of memory consumption concerns.

As it turns out, storing references to views using lazy is bad practice. Since onCreateView gets called many times in the lifecycle of the ArtistListFragment the same view wasn't being referenced that was currently inflated on the screen.

Removing all lazy instantiated views and accessing them directly with the Android Extensions solved my problem.

Upvotes: 0

aminography
aminography

Reputation: 22832

Try to use setOffscreenPageLimit for ViewPager to keep containing fragments as below:

viewPager.setOffscreenPageLimit(NavigationPageAdapter.NUM_PAGES)

Upvotes: 1

Related Questions