How can I add fragments to ViewPager2 in Xamarin.Android C#?

Marcell Zólyomi 46 Reputation points
2021-07-21T06:45:24.263+00:00

I want to make a Tab Layout with ViewPager2. I have already a working solution with ViewPager, but I have to change it to ViewPager2, because of some issue. I found some example, but these writed in Java, but I working with C#. I asking for help with following things: write a suitable adapter for ViewPager2, adding fragments to it, reach the new methods of it.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>

MainActivity OnCreate:

pager = FindViewById<ViewPager2>(Resource.Id.pager);
PagerAdapter adapter = new PagerAdapter(this);
adapter.AddFragment(new FragmentA());
adapter.AddFragment(new FragmentB());
adapter.AddFragment(new FragmentC());
pager.Adapter = adapter;
adapter.NotifyDataSetChanged();

Adapter:

public class PagerAdapter : FragmentPagerAdapter
{
public List<AndroidX.Fragment.App.Fragment> fragments = new List<AndroidX.Fragment.App.Fragment>();
public List<string> fragmentTitles = new List<string>();
public PagerAdapter(AndroidX.Fragment.App.FragmentManager fm) : base(fm)
{
}
public void AddFragment(AndroidX.Fragment.App.Fragment fragment, string title)
{
fragments.Add(fragment);
fragmentTitles.Add(title);
}
public override int Count => fragments.Count;
public override AndroidX.Fragment.App.Fragment GetItem(int position)
{
return fragments[position];
}
public override ICharSequence GetPageTitleFormatted(int position)
{
return new Java.Lang.String(fragmentTitles[position]);
}
}

FragmentA:

public class FragmentA : AndroidX.Fragment.App.Fragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.fragment_a_layout, container, false);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,354 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,938 questions
0 comments No comments
{count} votes

Accepted answer
  1. Graham McKechnie 416 Reputation points
    2021-07-22T01:47:30.65+00:00

    @Marcell Zólyomi

    You need to start your migration with a FragmentStateAdapter. Example below

    public class MonitorsViewPagerFragmentAdapter : FragmentStateAdapter  
        {  
            private readonly int itemCount;  
      
            //Refer to https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2/62184494#62184494   
            public MonitorsViewPagerFragmentAdapter(FragmentManager fragmentManager, Lifecycle lifecylce, int itemCount) : base(fragmentManager, lifecylce)  
            {  
                this.itemCount = itemCount;  
            }  
      
            // implement inherited abstract member ItemCount  
            public override int ItemCount => itemCount;  
      
            // implement inherited abstract member - CreateFragment of Viewpager2 replaces GetItem of ViewPager. Creating a new instance each time instead of reusing instances as was done in ViewPager  
            public override Fragment CreateFragment(int position)  
            {  
                return position switch  
                {  
                    0 => MonitorFragment.NewInstance(),  
                    1 => ContinuousMonitoringFragment.NewInstance(),  
                    2 => NonContinuousMonitoringFragment.NewInstance(),  
                    _ => null,  
                };  
            }  
        }  
    

    Then in your parent fragment in your OnViewCreated you would instantiate the FragmentStateAdapter like the following lines

    // FragmentStateAdapter - which calls CreateFragment for the three fragments - note the extra param ViewLifecycleOwner.Lifecycle   
    // Refer to https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2/62184494#62184494   
    // This is using the third of the three FragmentStateAdapter public constructors.  
    // Note - it is also an abstract class, so we can add out own params. e.g. passing the total number of fragments that the ViewPager2 will hold.  
    monitorsViewPagerFragmentAdapter = new MonitorsViewPagerFragmentAdapter(ChildFragmentManager, ViewLifecycleOwner.Lifecycle, 3);  
    

    Then for your viewPager2

    // Create a new ViewPager2.OnPageChangeCallback and then register with the ViewPager2 - must be unregistered in OnDestroy  
    monitorsViewPagerOnPageChangeCallback = new OnPageChangeCallback(Activity);  
    monitorsViewPager.RegisterOnPageChangeCallback(monitorsViewPagerOnPageChangeCallback);  
    monitorsViewPager.Orientation = ViewPager2.OrientationHorizontal;  
                  
    // We need all three fragments in memory, setting OffscreenPageLimit=2 will achieve that    
    monitorsViewPager.OffscreenPageLimit = 2; // ViewPager2.OffscreenPageLimitDefault;  
    monitorsViewPager.Adapter = monitorsViewPagerFragmentAdapter;  
    

    Next the tabs

    tabLayout.TabMode = TabLayout.ModeFixed;    
    tabLayout.TabGravity = TabLayout.GravityCenter;   
      
    TabLayoutMediator tabMediator = new TabLayoutMediator(tabLayout, monitorsViewPager, new TabConfigurationStrategy(Activity));  
    tabMediator.Attach();  
    

    An optional OnPageChangeCallback

    class OnPageChangeCallback : ViewPager2.OnPageChangeCallback  
    {  
        // Don't really need this as we are not using it.  
        private readonly Context context;  
      
        public OnPageChangeCallback(Context context)  
        {  
           this.context = context;  
         }  
      
         public override void OnPageSelected(int position)  
         {  
            //Log.Debug(logTag, "OnPageSelected - SelectedItem " + selectedItem.ToString());  
          }  
    }  
    

    And a TabConfigurationStrategy

    class TabConfigurationStrategy : Java.Lang.Object, TabLayoutMediator.ITabConfigurationStrategy  
    {  
       // using this for the tab titles  
       private readonly Context context;  
      
       public TabConfigurationStrategy(Context context)  
       {  
             this.context = context;  
       }  
      
       public void OnConfigureTab(TabLayout.Tab tab, int position)  
       {  
             tab.SetText(context.Resources.GetStringArray(Resource.Array.monitor_tab_titles)[position]);  
       }  
    }  
    

    See the following link for more information https://developer.android.com/training/animation/vp2-migration and follow the links in the code for more info.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. JarvanZhang 23,961 Reputation points
    2021-07-22T04:35:57.7+00:00

    Hello @Marcell Zólyomi ,​

    Welcome to our Microsoft Q&A platform!

    To migrate to Viewpager2, there are some important points to note:

    1. To use the ViewPager2 control in Xamarin.Android, we need to install the Xamarin.AndroidX.ViewPager2 nuget. And update/install Xamarin.Google.Android.Material package to use TabLayoutMediator, see below.
    2. When ViewPager used FragmentPagerAdapter to page through a small, fixed number of fragments, use FragmentStateAdapter with ViewPager2
    3. While TabLayout uses its own setupWithViewPager() method to integrate with ViewPager, it requires a TabLayoutMediator instance to integrate with ViewPager2.

    ##Update: Thanks to GrahamMcKechnie for the reminded, it's my mistake. I've updated the relevant code.

    Here is the sample code, you could refer to:

    public class ViewPager2_TestActivity : AppCompatActivity
    {
        public static List&lt;string&gt; fragmentTitles;
        TabLayout tabLayout;
        ViewPager2 pager;
    
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            // Create your application here
            SetContentView(Resource.Layout.layout_viewpager2);
    
            tabLayout = FindViewById&lt;TabLayout&gt;(Resource.Id.tabLayout);
            pager = FindViewById&lt;ViewPager2&gt;(Resource.Id.pager);
    
            CustomViewPager2Adapter adapter = new CustomViewPager2Adapter(this.SupportFragmentManager, this.Lifecycle);
    
            fragmentTitles = = new List&lt;string&gt;() { &#34;FragmentA&#34;, &#34;FragmentB&#34;, &#34;FragmentC&#34;};
    
            pager.Adapter = adapter;
            adapter.NotifyDataSetChanged();
    
            new TabLayoutMediator(tabLayout, pager, new CustomStrategy()).Attach();
        }
    }
    
    public class CustomViewPager2Adapter : FragmentStateAdapter
    {
        public CustomViewPager2Adapter(AndroidX.Fragment.App.FragmentManager fragmentManager, Lifecycle lifecycle) : base(fragmentManager, lifecycle)
        {
        }
        public override int ItemCount =&gt; 3;
    
        private AndroidX.Fragment.App.Fragment fragment = new AndroidX.Fragment.App.Fragment();
        public override AndroidX.Fragment.App.Fragment CreateFragment(int position)
        {
    
            switch (position)
            {
                case 0:
                    fragment = new ViewPage2FragmentA();
                    break;
                case 1:
                    fragment = new ViewPage2FragmentB();
                    break;
                case 2:
                    fragment = new ViewPage2FragmentC();
                    break;
            }
            return fragment;
        }
    }
    
    public class ViewPage2FragmentA : AndroidX.Fragment.App.Fragment
    {
        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            return inflater.Inflate(Resource.Layout.fragment_a_layout, container, false);
        }
    }
    
    public class CustomStrategy : Java.Lang.Object, ITabConfigurationStrategy
    {
        public void OnConfigureTab(TabLayout.Tab p0, int p1)
        {
            p0.SetText(ViewPager2_TestActivity.fragmentTitles[p1]);
        }
    }
    

    Check the doc: https://developer.android.com/training/animation/vp2-migration#migrate

    Best Regards,

    Jarvan Zhang


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.