相关文章推荐
高大的电影票  ·  Android ...·  12 月前    · 
刀枪不入的红薯  ·  Spring @Value ...·  1 年前    · 
爽快的冲锋衣  ·  ubuntu14.04 ...·  1 年前    · 
要出家的米饭  ·  SELECT TOP 1 1 FROM ...·  1 年前    · 

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);
						

@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.

As I understand it I have to make three separate Fragment.cs file, and one for an adapter and of course I have an activity, but I don't understand which one is my parent fragment, becaouse the ViewPager2 is on my activity, so I thought the parent is my activity not one of my fragment. Could you explain it to me?

@Marcell Zólyomi

If you look at the definition of FragmentStateAdapter you'll see there are 3 constructors. I'm using the 3rd ctor, because I'm calling it from a Fragment I therefore need to use ChildFragmentManager. In your case from an Activity you can use SupportFragmentManager using the same ctor.

I only have a single Activity in my app as I'm using Android's NavigationComponent which means fragments for all screens. Therefore in my case, it has to be the ChildFragmentManager.

The easiest way to set up a FragmentStateAdapter is to create a new class eg. public class TestViewPager2Adapter. Inherit TestViewPager2Adapter from FragmentStateAdapter and then let VS do the work for you.

So now you have red squiggles on FragmentStateAdapter

public class TestViewPagerAdapter : FragmentStateAdapter  

Right Click on the squiggly and choose Quick actions and it will suggest adding the using AndroidX.ViewPager2.Adapter - just accept the suggestion.

Next, you'll have another squiggly on TestViewPager2Adapter. Right-click on that squiggly and accept the suggestion implement abstract class. You end up with

public class TestViewPagerAdapter : FragmentStateAdapter  
        public override int ItemCount => throw new NotImplementedException();  
        public override AndroidX.Fragment.App.Fragment CreateFragment(int p0)  
            throw new NotImplementedException();  

VS has now written the important parts of the code for you.

@Marcell Zólyomi
I just read Javan's answer. So I thought I should point out the following.

I don't believe his technique of returning an already created fragment is correct. The migration docs specifically say Make sure that your new createFragment() method always supplies a new fragment instance each time the function is called instead of reusing instances.

Hence I'd suggest your CreateFragment should be written the way I showed in my example. In other words, do not use a List<AndroidX.Fragment.App.Fragment> fragments in the FragmentStateAdapter.

@Marcell Zólyomi

I tried your way, but the NewInstance method doesn't apper. You made it yourself?

Yes here is an example of the MonitorFragment

public MonitorFragment() { }  
public static MonitorFragment NewInstance()  
	MonitorFragment fragment = new MonitorFragment();  
       return fragment;  

Android requires a parameterless constructor for a Fragment, which it will automatically use on a configuration change. Hence the use of methods like GetInstance or NewInstance. They then allow you to pass in parameters to the Fragment if required, or you could pass in a Bundle.

Hello @Marcell Zólyomi ,​

Welcome to our Microsoft Q&A platform!

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

  • 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.
  • When ViewPager used FragmentPagerAdapter to page through a small, fixed number of fragments, use FragmentStateAdapter with ViewPager2
  • 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.