How to create a tabbed dialog in Xamarin.Android
Tags: xamarin, android, dialog, csharp
Published: 2022-01-25 08:42:18

Every once in a while, we need to fulfill certain requirements. Recently, I had to implement a tabbed dialog in Xamarin.Android. Previously, this was relatively easy using a `TabHost` ([RIP](https://developer.android.com/reference/android/widget/TabHost)), but now, you must use a `TabLayout` and a `ViewPager2`, which at first glance sounds like an easy deal, but it became quite complex.

Now, the steps to creating one are the following ones:

### Step 1.

Create an XML for the main dialog container (**main_tabbed_dialog.xml**) with the `TabLayout`, the `ViewPager2`, a `TextView` for the title, and a `LinearLayout` for the buttons.

```xml
﻿<?xml version="1.0" encoding="UTF-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:orientation="vertical">

	<TextView
		android:layout_height="wrap_content"
		android:layout_width="match_parent"
		android:text="Title"
		style="@android:style/TextAppearance.DialogWindowTitle"
		android:textStyle="bold"
		android:paddingTop="?attr/dialogPreferredPadding"
		android:paddingLeft="?attr/dialogPreferredPadding"
		android:paddingRight="?attr/dialogPreferredPadding"></TextView>

	<com.google.android.material.tabs.TabLayout
		android:id="@id/tabLayout"
		android:layout_width="match_parent"
		android:layout_height="wrap_content" />

	<androidx.viewpager2.widget.ViewPager2
		android:id="@id/masterViewPager"
		android:layout_width="match_parent"
		android:layout_height="match_parent" />
	
	<RelativeLayout
		android:layout_width="match_parent"
		android:layout_height="wrap_content"
		android:orientation="vertical">

		<Button
			android:id="@id/btnOK"
			android:layout_width="wrap_content"
			android:padding="5dp"
			android:text="OK"
			android:layout_height="wrap_content"
			android:layout_alignParentRight="true"
			android:layout_centerInParent="true"
			style="@style/Widget.AppCompat.Button.Borderless.Colored" />
	</RelativeLayout>
</LinearLayout>
```

> **Note:**
> The buttons and the title are here because there is a conflict if you add them programmatically. They are not displayed.

### Step 2.

Create your views with some XML like these ones:

**view1.xml**

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@color/white"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:fillViewport="true">
	<LinearLayout
		android:orientation="vertical"
		android:layout_width="match_parent"
		android:layout_height="wrap_content"
		android:paddingLeft="10dp"
		android:paddingRight="10dp">
		<TextView
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="view1"
			android:layout_marginBottom="10dp"
			android:textSize="16dp"
			android:layout_marginTop="10dp" />
	</LinearLayout>
</ScrollView>
```

**view2.xml**

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@color/white"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:fillViewport="true">
	<LinearLayout
		android:orientation="vertical"
		android:layout_width="match_parent"
		android:layout_height="wrap_content"
		android:paddingLeft="10dp"
		android:paddingRight="10dp">
		<TextView
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="view2"
			android:layout_marginBottom="10dp"
			android:textSize="16dp"
			android:layout_marginTop="10dp" />
	</LinearLayout>
</ScrollView>
```

### Step 3.

Create your `DialogFragment` class:

```csharp
public partial class MainDialogFragment : DialogFragment
{
	static Context mContext;

	public static MainDialogFragment NewInstance(Context context)
	{
		mContext = context;
		return new MainDialogFragment();
	}

	public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
	{
		var view = inflater.Inflate(Resource.Layout.main_tabbed_dialog, container, false);

		var btnOK = view.FindViewById<Button>(Resource.Id.btnOK);

		btnOK.Click = BtnOK_Click;

		return view;
	}

	private void BtnOK_Click(object sender, EventArgs e)
	{
		Dismiss();
	}

	public override void OnViewCreated(View view, Bundle savedInstanceState)
	{
		base.OnViewCreated(view, savedInstanceState);

		ViewPager2 viewPager2 = view.FindViewById<ViewPager2>(Resource.Id.masterViewPager);

		TabLayout tabLayout = view.FindViewById<TabLayout>(Resource.Id.tabLayout);

		var adapter = new MainDialogAdapter(ChildFragmentManager, ViewLifecycleOwner.Lifecycle, 2);

		viewPager2.Adapter = adapter;

		tabLayout.TabMode = TabLayout.ModeFixed;
		tabLayout.TabGravity = TabLayout.GravityCenter;

		TabLayoutMediator tabMediator = new(tabLayout, viewPager2, new TabFullFilterConfigurationStrategy(Activity));
		tabMediator.Attach();
	}
}

//In this class, in the method OnConfigureTab, you change the Tab Titles based on its location
public class TabFullFilterConfigurationStrategy : Java.Lang.Object, TabLayoutMediator.ITabConfigurationStrategy
{
	private readonly Context context;

	public TabFullFilterConfigurationStrategy(Context context)
	{
		this.context = context;
	}

	public void OnConfigureTab(TabLayout.Tab tab, int position)
	{
		tab.SetText(position == 0 ? "View 2" : "View 1");
	}
}

public class MainDialogAdapter : FragmentStateAdapter
{
	public MainDialogAdapter(FragmentManager fragmentManager, Lifecycle lifecylce, int itemCount) : base(fragmentManager, lifecylce)
	{
		this.itemCount = itemCount;
	}

	private readonly int itemCount;
	public override int ItemCount => itemCount;

	public FragmentActivity Fragment { get; }

	public override Fragment CreateFragment(int position)
	{
		return position switch
		{
			0 => View1Fragment.NewInstance(),
			_ => View2Fragment.NewInstance()
		};
	}
}
```

### Step 4.

Create your view classes:

```csharp
public class View1 : Fragment
{
	public static View1 NewInstance()
	{
		return new View1 { Arguments = new Bundle() };
	}

	public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
	{
		base.OnCreateView(inflater, container, savedInstanceState);

		var view = inflater.Inflate(Resource.Layout.view1, container, false);

		return view;
	}
}
```

```csharp
public class View2 : Fragment
{
	public static View2 NewInstance()
	{
		return new View2 { Arguments = new Bundle() };
	}

	public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
	{
		base.OnCreateView(inflater, container, savedInstanceState);

		var view = inflater.Inflate(Resource.Layout.view2, container, false);

		return view;
	}
}
```

### Step 5.

Show your dialog:

```csharp
var fm = SupportFragmentManager;
var dialog = MainDialogFragment.NewInstance(this);
dialog.Show(fm, "dialog");
```

And that's all.

This is an example of a fully functional case:

![preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h9v5k9bhid6s84dh4u3p.png)

You can see the fully functional app with the dialogs here:

https://play.google.com/store/apps/details?id=tk.supernova.tipsal

> **Note:**
> If you need to share data between the fragments and the main activity, you can follow this tutorial:
> https://stackoverflow.com/questions/16036572/how-to-pass-values-between-fragments

### Follow me on:

|  Personal  |  LinkedIn  |YouTube|Instagram|Cyber Prophets|Sharing Your Stories|
|:----------|:----------|:------------:|:------------:|:------------:|:------------:|
|[![Personal](https://raw.githubusercontent.com/FANMixco/federiconavarrete/master/img/favicons/favicon.png)](https://federiconavarrete.com)|[![LinkedIn](https://i.stack.imgur.com/idQWu.png)](https://www.linkedin.com/in/federiconavarrete)|[![YouTube](https://i.stack.imgur.com/CFPMR.png)](https://youtube.com/c/FedericoNavarrete)|[![Instagram](https://i.stack.imgur.com/PIfqY.png)](https://www.instagram.com/federico_the_consultant)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/cyber-prophets)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/sharing-your-stories)|

[![sponsor me](https://raw.githubusercontent.com/FANMixco/Xamarin-SearchBar/master/bmc-rezr5vpd.gif)](https://www.buymeacoffee.com/fanmixco)