Every once in a while, we need to fulfill certain requirements. Recently, I had to implement a tabbed dialog in .NET for Android or Xamarin.Android. Previously, this was relatively easy using a TabHost
(RIP), 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 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 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 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:
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:
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;
}
}
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:
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:
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
Top comments (0)