I'm using viewpager 2 now.
The views of the fragments are all the same, so I'm using the same fragments.
And the data is shown through recyclerview in the fragment.
All fragments set different data list.
OnResume retrieves new data through a network call and performs recyclerviewAdapter.submitlist.
It shows the same data view during swiping.
If onResume does not update the data, it shows a different view during swiping.
class CommunityViewPagerAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle,
) : FragmentStateAdapter(fragmentManager, lifecycle) {
private val fragments = listOf(
TechCommunityFragment.newInstance("Backend"),
TechCommunityFragment.newInstance("Frontend"),
TechCommunityFragment.newInstance("Android"),
TechCommunityFragment.newInstance("IOS"),
TechCommunityFragment.newInstance("DataScience"),
TechCommunityFragment.newInstance("DataAnalysis"),
TechCommunityFragment.newInstance("Algorithm"),
TechCommunityFragment.newInstance("DataStructure"),
TechCommunityFragment.newInstance("Network"),
TechCommunityFragment.newInstance("OperatingSystem"),
)
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
private fun initView() = with(binding) {
viewPager.adapter= CommunityViewPagerAdapter(childFragmentManager, lifecycle)
TabLayoutMediator(techTablayout, viewPager) { tab, position ->
tab.text = mainTabArray[position]
}.attach()
}
override fun onResume() {
super.onResume()
techStack = arguments?.getString(TECH_STACK) ?: ""
callAPI()
}
Related
My app contains viewpager and tablayout. It requests data from the server, sorts and gives each tab its own set of information. I don't want to have a request to the network every time I select a tab, so I made a host fragment that makes a request to the network and creates tabs. But when the data arrives, the view is already rendered. I give the data through the bundle to the ViewPagerAdapter. The first tab does not update the data. How can I tell the adapter to redraw a particular element?
HostFragment
private val tabsTitles by lazy(LazyThreadSafetyMode.NONE) {
listOf("All", "Analytics", "Android", "Management")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val departmentTabLayout = view.findViewById<TabLayout>(R.id.departmentTabLayout)
val departmentViewPager = view.findViewById<ViewPager2>(R.id.departmentViewPager)
val adapter = TabsAdapter(childFragmentManager, lifecycle)
departmentViewPager.adapter = adapter
attachTabs(departmentTabLayout, departmentViewPager)
departmentHostViewModel.listUsers.observe(viewLifecycleOwner, {
adapter.getUsers(it.items)
//adapter.notifyDataSetChanged()
})
}
private fun attachTabs(departmentTabLayout: TabLayout, departmentViewPager: ViewPager2) {
TabLayoutMediator(departmentTabLayout, departmentViewPager) { tab, position ->
tab.text = tabsTitles[position]
}.attach()
}
TabsAdapter
class TabsAdapter(
fm: FragmentManager,
lifecycle: Lifecycle,
) : FragmentStateAdapter(fm, lifecycle) {
private val listDepartments = listOf(
"all",
"analytics",
"android",
"management"
)
private val listUsers: MutableList<User> = mutableListOf()
override fun createFragment(position: Int): Fragment {
val departmentFragment = DepartmentFragment()
return when (position) {
0 -> {
departmentFragment.arguments = bundleOf(DepartmentFragment.LIST_USERS to listUsers)
return departmentFragment
}
else -> {
departmentFragment.arguments =
bundleOf(DepartmentFragment.LIST_USERS to listUsers.filter { it.department == listDepartments[position] })
return departmentFragment
}
}
}
override fun getItemCount(): Int {
return listDepartments.count()
}
fun getUsers(listUsers2: List<User>) {
listUsers.addAll(listUsers2)
notifyDataSetChanged()
}
}
Hi guys I am using ViewPager with FragmentStateAdapter. I have an option for the user, in one of the added fragments, to recreate the activity (a method calls: activity.recreate()). However if the activity is recreated I get the following error:
java.lang.IllegalStateException: Design assumption violated.
at androidx.viewpager2.adapter.FragmentStateAdapter.placeFragmentInViewHolder(FragmentStateAdapter.java:287)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:276)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:67)
at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:7556)
at androidx.recyclerview.widget.RecyclerView$5.addView(RecyclerView.java:860)
at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:107)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:8601)
Here is my FragmentStateAdapter:
class MainViewPagerAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
var fragmentList: ArrayList<Fragment> = arrayListOf()
override fun getItemCount(): Int = fragmentList.size
//Put all the fragments needed based on position
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
fun add(fragment: Fragment) {
fragmentList.add(fragment)
notifyDataSetChanged()
}
fun addByPosition(position: Int, fragment: Fragment) {
fragmentList.add(position, fragment)
notifyDataSetChanged()
}
fun remove(index: Int) {
fragmentList.removeAt(index)
notifyDataSetChanged()
}
override fun getItemId(position: Int): Long {
return fragmentList[position].hashCode().toLong()
}
override fun containsItem(itemId: Long): Boolean {
return fragmentList.contains(itemId)
}}
I am using:
implementation "androidx.viewpager2:viewpager2:1.0.0"
and I have tried the followig:
remove getItemId() -> works but I need this method to be overridden in order to retain a proper position of all the elements when add/remove a fragment from the adapter
tried to save the list of fragments inside onSaveInstanceState(outState: Bundle) and pass it back to the adapter but I get the same error.
What am I doing wrong? What is the best way to deal with this error?
This question already has answers here:
Get current fragment with ViewPager2
(18 answers)
Closed 1 year ago.
i used this ViewPager2 dependency:
implementation 'androidx.viewpager2:viewpager2:1.0.0'
then i wrote code and make Adapter for FragmentStateAdapter like below:
private inner class ViewPagerAdapter(fragmentManager: FragmentManager,
lifecycle: Lifecycle, config: TribunConfig?)
: FragmentStateAdapter(fragmentManager, lifecycle) {
private val config: TribunConfig?
override fun createFragment(position: Int): Fragment {
return TribunNewsFragment.newInstance(if (menuTribun!!.get(position) == "News") "home"
else menuTribun!!.get(position)!!.replace(" ", "").toLowerCase(), config)!!
}
override fun getItemCount(): Int {
return menuTribun!!.size
}
init {
this.config = config
}
}
an in method onCreate i implement some code related viewpager2 and its adapter like below:
viewPager2!!.offscreenPageLimit = 1
viewPager2!!.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, config)
TabLayoutMediator(tabLayout!!, viewPager2!!, TabLayoutMediator.TabConfigurationStrategy { tab: TabLayout.Tab?, position: Int -> tab!!.text = menuTribun!!.get(position) }).attach()
But i have problem when i want to make swipeRefresh. My swipeRefresh needs current fragment to access the method inside fragment. But there is NO METHOD getItem in FragmentStateAdapter. Anyone can help me?
Create property in your adapter to save all your fragments
Save current position
Get fragment from fragment list (property) by position when you need it
Something like this:
val fragments: MutableList<Fragment> = mutableListOf()
private val config: TribunConfig?
override fun createFragment(position: Int): Fragment {
val fragment = TribunNewsFragment.newInstance(if (menuTribun!!.get(position) == "News") "home"
else menuTribun!!.get(position)!!.replace(" ", "").toLowerCase(), config)!!
fragments.add(fragment)
return fragment
}
Parent:
yourTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
val fragment = adapter.fragments[tab!!.position]
// cast fragment to your fragment class and do what you want
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
Also you can use when or if-else for your fragments position or create base class with methods you need and cast getting fragment to it.
Following #Oleg's answer, you can access the Fragments like this in your Adapter instead of creating a local list and adding each fragment on createFragment method:
private val fragmentList: List<TribunNewsFragment>
get() {
return fragment.childFragmentManager.fragments.filterIsInstance<TribunNewsFragment>()
}
It's important to use childFragmentManager instead of fragmentManager if you are creating the ViewPager inside another fragment.
And it survives configuration changes, regarding #lawonga's comment on the answer's topic.
I have a tab layout in which I have included five fragments, but when i click on tabs, the fragments layout is not showed. In my adapter I've extended FragmentPagerAdapter which has FragmentManager and a variable behaviour.
class ViewPagerAdapter(#NonNull fm:FragmentManager, behaviour:Int):
FragmentPagerAdapter(fm, behaviour) {
private val tabs:Array<Fragment> = arrayOf(
Category1Fragment(),
Category2Fragment(),
Category3Fragment(),
Category4Fragment(),
Category5Fragment()
)
#NonNull
override fun getItem(position: Int): Fragment {
return tabs[position]
}
override fun getCount(): Int {
return tabs.size
}
#Nullable
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> "Bags"
1 -> "Watches"
2 -> "Shoes"
3 -> "Glasses"
4 -> "Audio"
else -> null
}
}
}
and in my activity I've called adapter through viewpager, but when I debug is telling me that adapter is null, here's my activity code:
val adapter = ViewPagerAdapter(supportFragmentManager, tab_layout.selectedTabPosition)
val viewPager = findViewById<ViewPager>(R.id.view_pager)
viewPager.adapter = adapter
val tab = findViewById<TabLayout>(R.id.tab_layout)
tab.setupWithViewPager(view_pager)
// -------------------------------------------------------------------------------- //
tab.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(p0: TabLayout.Tab?) {
}
override fun onTabUnselected(p0: TabLayout.Tab?) {
}
override fun onTabSelected(p0: TabLayout.Tab?) {
viewPager.currentItem = tab.selectedTabPosition
}
})
In the previous versions of android I did the same, but in androidX things are something different. I tried also to use ViewPager2, but it was confusing.
Any help is appreciated!
I have a Pager Adapter to load server images as like below:
class ImagePagerAdapter(var imageList: List<ProductImageModel>, var fragmentManager: FragmentManager) :
FragmentPagerAdapter(fragmentManager) {
override fun getItem(position: Int): Fragment {
val fragment = ImagePagerFragment()
fragment.arguments = Bundle().apply {
putString(PAGER_IMAGE_URL, imageList[position].image)
}
return fragment
}
override fun getCount(): Int {
return imageList.size
}
}
I want to know any option to addtoBackStack(null) while creating a new Fragment inside the pagerAdapter.
The current code, automatically adding the fragments to the backstack. So it's making a huge issue while the user presses the back button. I have added another logic to filter the fragments in the back press.