Tuesday 5 January 2021

RecyclerView with StaggeredGridLayoutManager in ViewPager, arranges items automatically when going back to fragment

I am using Navigation component in my App, using google Advanced Sample(here). my problem is when going back to a fragment, RecyclerView rearranges items and moves highest visible items so that top of those item align to top of recyclerview. please see this:

enter image description here

before going to next fragment:

enter image description here

and after back to fragment:

enter image description here

this problem is matter because some times clicked item goes down and not seen until scroll down. how to prevent this behavior?

please consider:

  • this problem exist if using navigation component to change fragment. if start fragment using supportFragmentManager.beginTransaction() or start another activity and then go to this fragment it is OK. but if I navigate to another fragment using navigation component this problem is exist.(maybe because of recreating fragment)

  • also this problem exist if using fragment in ViewPager. i.e recyclerView is in a fragment that handle with ViewPagerAdapter and viewPager is in HomeFragment that opened with Navigation component. if recyclerView is in HomeFragment there is no problem.

  • no problem with LinearLayoutManager. only with StaggeredGridLayoutManager.

  • there is not difference if using ViewPager2 and also FragmentStatePagerAdapter

  • I try to prevent recreate of fragment(by this solution) but not solved.

code is:

class HomeFragment : BaseFragment() {

    override val layout = R.layout.fragment_home

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

        setupViewPager()
    }

    private fun setupViewPager() {

        val tab1 = tabLayout.newTab()
        tab1.text = "Test2"
        tabLayout.addTab(tab1, 0, true)

        val tab2 = tabLayout.newTab()
        tab2.text = "Test1"
        tabLayout.addTab(tab2, 1, true)

        val adapter = StreamPagerAdapter(
            childFragmentManager, listOf(
                ApiUrls.FRIENDS_STREAM_URL,
                ApiUrls.CHOICES_STREAM_URL,
                ApiUrls.LATEST_STREAM_URL,
            )
        )

        homeViewPager.adapter = adapter
    }
}

pager adapter:

class StreamPagerAdapter(fragmentManager: FragmentManager, val urlList: List<String>) :
    FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
    
    override fun getCount(): Int {
        return urlList.size
    }

    override fun getItem(position: Int): Fragment {
        return TestFragment().newInstance(urlList[position])
    }

}

fragment:

class TestFragment : Fragment() {

    fun newInstance(streamUrl: String): TestFragment {
        val fragment = TestFragment()
        val args = Bundle()
        args.putString(Arguments.POST_LIST_URL, streamUrl)
        fragment.arguments = args
        return fragment
    }

    val viewModel by viewModel<HomeViewModel>()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragmetn_test, container, false)
    }

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

        postListView.apply {
            layoutManager = StaggeredGridLayoutManager(
                2, StaggeredGridLayoutManager.VERTICAL
            ).apply {
                gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
            }
            setHasFixedSize(true)

            adapter = TestListAdapter()
        }
    }
}

RecyclerView adapter:

class TestListAdapter: RecyclerView.Adapter<TestListAdapter.ListViewHolder>(){

    private val itemList = listOf(
        TestModel(Color.BLUE, 340),
        TestModel(Color.CYAN, 500),
        TestModel(Color.DKGRAY, 450),
        TestModel(Color.GREEN, 660),
        TestModel(Color.WHITE, 620),
        TestModel(Color.YELLOW, 550),
        TestModel(Color.BLACK, 390),
        TestModel(Color.GRAY, 430),
        TestModel(Color.RED, 230),
        TestModel(Color.MAGENTA, 420),
        TestModel(Color.GREEN, 360),
    )

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val view =
            DataBindingUtil.inflate<TestListItemBinding>(
                inflater,
                R.layout.test_list_item,
                parent,
                false
            )
        return ListViewHolder(binding = view)
    }

    override fun onBindViewHolder(holder: ListViewHolder, position: Int) {


        val testView = holder.itemView.testView

        testView.setBackgroundColor(itemList[position].color)

        val params: ViewGroup.LayoutParams = testView.layoutParams
        params.height = itemList[position].height

        holder.binding.executePendingBindings()
    }


    override fun getItemCount() = itemList.size

    class ListViewHolder(var binding: TestListItemBinding) : RecyclerView.ViewHolder(binding.root)

    data class TestModel(val color: Int, val height: Int)

}


from RecyclerView with StaggeredGridLayoutManager in ViewPager, arranges items automatically when going back to fragment

No comments:

Post a Comment