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.
Related
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()
}
What i have: Application with BottomNavigation using Android Navigation Component. One of fragments - fragment with other two fragments with recyclerviews in viewPager2. When i navigating to another tab and go back, recyclerview is fully redrawing content
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.schedulePager.apply {
isSaveFromParentEnabled = true
offscreenPageLimit = 2
adapter = ScheduleViewPager(childFragmentManager, lifecycle)
setPageTransformer(ZoomOutPageTransformer())
}
... other stuff
}
class ScheduleViewPager(
fragmentManager: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager,lifecycle) {
override fun getItemCount(): Int = 2
override fun createFragment(position: Int): Fragment = PageFragment().apply { arguments = SchedulePageFragmentArgs.bundle(position) }
}
if i'm using FragmentActivity instead of FragmentManager, content in fragments is not redrawing, but i need to use FragmentManager fof access ViewModel from fragment with viewPager
class PageFragment : BaseFragment<FragmentPageBinding>(), SharedPreferences.OnSharedPreferenceChangeListener {
private val viewModel: ScheduleViewModel by viewModels({requireParentFragment()})
I have a viewpager2 with FragmentStateAdapter() adapter inside it. I also have a tab layout with 4 tabs. I use a single fragment for all tab. that is named AllOrdersTab.
in my architecture I just send different value to load different API data to AllOrdersTab fragment.
when each tab layout is selected , a fragment created and works fine for the first time for all 4 fragments. after that if I swipe back to previous tab it is not created or refreshed again.
I want to recreate the fragment or a way to call API again when swiping between tabs. I also read this page.
FragmentStateAdapter not recreating currentFragment after notifyDataSetChanged
I tried to do this. so I decided to create 4 instance of Allorderstab() fragment. but never work for me because I guess hash codes of fragments are same.
ViewPager2 Adapter:
class ViewPagerOrdersAdapter(fm: FragmentManager,val listFragments:MutableList<Fragment>, viewlifecycler: Lifecycle) : FragmentStateAdapter(fm, viewlifecycler)
{
override fun getItemCount(): Int
{
return listFragments.size
}
override fun createFragment(position: Int): Fragment {
val args = Bundle()
when (position) {
1 -> {
args.putString("KEY_ID", "inProgress")
listFragments[position].arguments = args
return listFragments[position]
}
2 -> {
args.putString("KEY_ID", "cancel")
listFragments[position].arguments = args
return listFragments[position]
}
3 ->
{
args.putString("KEY_ID","deliver")
listFragments[position].arguments=args
return listFragments[position]
}
else -> {
args.putString("KEY_ID", "all")
listFragments[position].arguments = args
return listFragments[position]
}
}
}
override fun getItemId(position: Int): Long {
return listFragments[position].hashCode().toLong()
}
override fun containsItem(itemId: Long): Boolean {
return listFragments.find {it.id.hashCode().toLong() == itemId } != null
}
}
Here I created 4 instance of Allorderstab() Fragment.
Set ViewPager2 Adapter
MainFragment:
val fragments:MutableList<Fragment> = mutableListOf(Allorderstab(), Allorderstab(), Allorderstab(), Allorderstab())
vp.setAdapter(ViewPagerOrdersAdapter(this.childFragmentManager,fragments, lifecycle))
AllOrderstab Fragment:
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?, savedInstanceState: Bundle?): View? {
val bundle = arguments
bundle?.let {
val myStatus = bundle.getString("KEY_ID")
myStatus?.let{
//getting history of each tab orders - calling API
myviewModel.getOrdersHistory(tempTn,myStatus)
}
}
}
All in all I don't know how to refresh while swiping between tabs . if I have a single fragment for all 4 tabs.
For those who wasted a day for this problem like me , wanting to call API each time for refreshing data, Move your code to onResume function. fortunately it is run each time your fragment visible.
AllOrderstab Fragment:
override fun onResume() {
val bundle = arguments
bundle?.let {
val myStatus = bundle.getString("KEY_ID")
myStatus?.let{
//getting history of all orders
myviewModel.getOrdersHistory(tempTn,myStatus)
}
}
super.onResume()
}
You have 4 different instances of the same tab. If you need to refresh the tabs everytime the user navigates to your tab you need to add a PageChangeListener to your tabs view. Then whenever you change the page you need to notify the fragment that it has come to foreground you can do so by calling a method on Allorderstab class and then refreshing the data from this method.
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 am using Bottom Navigation Activity in my project. Which contains following activities. I am using kotlin Language. I am not sure how to route from one fragment to another fragment in bottom navigation activity. Any help is appreciated.
Code:
https://github.com/joshvapeter/KotlinBottomNavigationNew
Activies
1)One Main Activity
2)Two Fragments
3)Two Adapter Class
4)Two Layout files
Expectation
When I click the Fragment One Recycler View Item then It need to automatically route it to Fragment two Recycler View Item in the next bottom tab. Below is the code, I am using in my project.
Code
Fragment One Adapter
itemView.setOnClickListener {
Log.d("Fragment One Clicked","Fragment One Clicked")
//FragmentTwo()
}
MainActivity
fun ShowFragmentOne() {
val transaction = manager.beginTransaction()
val fragment = FragmentOne()
transaction.replace(R.id.one, fragment)
transaction.addToBackStack(null)
transaction.commit()
isFragmentOneLoaded = true
}
fun ShowFragmentTwo() {
val transaction = manager.beginTransaction()
val fragment = FragmentTwo()
transaction.replace(R.id.two, fragment)
transaction.addToBackStack(null)
transaction.commit()
isFragmentOneLoaded = false
}
I don't have enough reputation to add a comment. So on the basis of my understanding, i am writing this answer. If my understanding is wrong, please comment.
From what i can understand you want to move to FragmentTwo when an item is clicked in RecyclerView in FragmentOne. You can achieve it in following way:
FragmentOne:
fun onItemSelected(item:MyModel){
(activity as MainActivity).showFragmentTwo(item)
}
FragmentOneAdapter:
class FragmentOneAdapter(val fragment:FragmentOne,val myList:ArrayList<MyModel>):RecyclerView.Adapter<MyViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): MyViewHolder {
//your code to create view holder
}
override fun onBindViewHolder(p0: MyViewHolder, p1: Int) {
p0.bindItem(myList[p1], fragment)
}
override fun getItemCount(): Int {
return myList.size
}
class MyViewHolder(view:View):RecyclerView.ViewHolder(view){
fun bindItem(item:MyModel,frag:FragmentOne)=with(itemView){
setOnClickListener{
frag.onItemSelected(item)
}
}
}
}