7

I have an app with four pages, and I want it to look similar to my (non-Xamarin) iOS app, so to have toolbar at the bottom. Here is my MainPage.xaml file:

<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XaBLE1"
             x:Class="XaBLE1.MainPage"
            Title="Safe-T Sim" HeightRequest="768" WidthRequest="512" 

            BarBackgroundColor="#F1F1F1"
            BarTextColor="Gray"
            xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
            android:TabbedPage.ToolbarPlacement="Bottom"
            android:TabbedPage.BarItemColor="#666666"
            android:TabbedPage.BarSelectedItemColor="Black"
            >

    <NavigationPage Title="Test" Icon="ElectTest.png"
                    HasNavigationBar="False">
        <x:Arguments>
            <local:TestPage />
        </x:Arguments>
    </NavigationPage>
    <NavigationPage Title="Review" Icon="Review.png"
                    HasNavigationBar="False">
        <x:Arguments>
            <local:ReviewPage />
        </x:Arguments>
    </NavigationPage>
    <NavigationPage Title="Setup" Icon="Gear.png"
                    HasNavigationBar="False">
        <x:Arguments>
            <local:SetupPage />
        </x:Arguments>
    </NavigationPage>
    <NavigationPage Title="Info" Icon="Info.png"
                    HasNavigationBar="False">
        <x:Arguments>
            <local:InfoPage />
        </x:Arguments>
    </NavigationPage>
</TabbedPage>

I don't care for the current look-and-feel on Oreo, which is to make the selected page tab larger and put the title, pushing the other tabs aside and removing the page title.
Bottom Tab Bar with 2nd page selected

Is there anyway to disable this behavior, and let it just be 4 tabs. Note that this behavior does not happen if there are 3 tabs -- there is only darkening and slight enlarging of the icon & text, but both are visible.

EDIT: I tried the answer suggested in the comments, but as noted I'm not sure this is trying to solve the same problem, and in any case does not change the behavior.

bobwki
  • 794
  • 6
  • 22
  • duplicate of solved https://stackoverflow.com/questions/45820704/xamarin-forms-disable-swipe-between-pages-in-tabbedpage/47039814#47039814 – Nick Kovalsky Aug 16 '18 at 06:57
  • 1
    Possible duplicate of [Xamarin Forms Disable swipe between pages in TabbedPage](https://stackoverflow.com/questions/45820704/xamarin-forms-disable-swipe-between-pages-in-tabbedpage) – Nick Kovalsky Aug 16 '18 at 06:58
  • First -- I see now that when I change pages the pages swipe across -- I didn't notice that before, and while it's not desired, that is OK with me. My issue is with the tab at the bottom that came in with Forms 3.1, and getting them to not slide over. Maybe these are related. I tried the codebehind, which forced me to add using Xamarin.Forms.PlatformConfiguration.AndroidSpecific and then resolve the abiguity for TabbedPage -- this did not change the behavior. The XAML change also did not change the behavior. – bobwki Aug 16 '18 at 19:30
  • "I don't care for the current look-and-feel on Oreo" Maybe, but it may be easier for your users if you conform to the expected Android design. – Andy Aug 18 '18 at 16:43
  • @Andy good point. In actuallity, it's not that I don't care for it, it is that my team feels it would be better for Android app to be very similar to iOS app, which has a 1 year lead on it. – bobwki Aug 21 '18 at 22:54
  • We have an app we developer on iOS and Android. The previous app which we are replacing was based on Cordova, and so didn't match iOS or Android design. One of the reasons we chose XF is specifally so that our iOS version looks and feels like an iOS app, and the Android one looks and feels like Android. I'm not saying create a separate unique UI for your Android app, but if you stick to the XF controls and let it put tabs on the bottom for iOS and at the top for android (with scrolling), users on both platforms would have an easier time with it. – Andy Aug 21 '18 at 22:58
  • More to the point, I absolute hate the user experience on iOS/Mac, and that was a major reason I use an Android. So I'd really hate an Android app that looked and felt like an iOS one. I'm sure there are plenty of people that are the opposite. (Actually, my favorite was WP8.1 and after some polish W10M, but those aren't really options anymore). Just something to think about. – Andy Aug 21 '18 at 22:59

3 Answers3

11

It looks like you are looking for this (not implemented yet) feature: [Enhancement] Implement fixed mode for Bottom Navigation Bar Android (Github)

I could solve it following this James Montemagno tutorial: Removing BottomNavigationView’s Icon Shifting in Xamarin.Android and implementing my own Tabbed Page custom renderer:

using Android.Content;
using Android.Support.Design.Internal;
using Android.Views;
using FixedTabbedPage.Droid.CustomRenderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;

[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedPageRenderer))]
namespace FixedTabbedPage.Droid.CustomRenderers
{
    public class CustomTabbedPageRenderer : TabbedPageRenderer
    {
        public CustomTabbedPageRenderer(Context context) : base(context) { }

        protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
        {
            base.OnElementChanged(e);

            if (ViewGroup != null && ViewGroup.ChildCount > 0)
            {
                BottomNavigationMenuView bottomNavigationMenuView = FindChildOfType<BottomNavigationMenuView>(ViewGroup);

                if (bottomNavigationMenuView != null)
                {
                    var shiftMode = bottomNavigationMenuView.Class.GetDeclaredField("mShiftingMode");

                    shiftMode.Accessible = true;
                    shiftMode.SetBoolean(bottomNavigationMenuView, false);
                    shiftMode.Accessible = false;
                    shiftMode.Dispose();

                    for (var i = 0; i < bottomNavigationMenuView.ChildCount; i++)
                    {
                        var item = bottomNavigationMenuView.GetChildAt(i) as BottomNavigationItemView;
                        if (item == null) continue;                         

                        item.SetShiftingMode(false);
                        item.SetChecked(item.ItemData.IsChecked);
                    }

                    if (bottomNavigationMenuView.ChildCount > 0) bottomNavigationMenuView.UpdateMenuView();
                }
            }
        }

        private T FindChildOfType<T>(ViewGroup viewGroup) where T : Android.Views.View
        {
            if (viewGroup == null || viewGroup.ChildCount == 0) return null;

            for (var i = 0; i < viewGroup.ChildCount; i++)
            {
                var child = viewGroup.GetChildAt(i);

                var typedChild = child as T;
                if (typedChild != null) return typedChild;

                if (!(child is ViewGroup)) continue;

                var result = FindChildOfType<T>(child as ViewGroup);

                if (result != null) return result;
            }

            return null;
        }
    }
}

You only have to add this code to your Android solution (refactoring namespaces) and here is the result:

Bottom Fixed TabbedPage on Android

J. Aguilar
  • 194
  • 5
  • Wow! That worked perfectly, once I figure out where to put what. I added this as a new class to my "Android" project. I put it into my current namespace (in my case, XaBLE1.Droid), and then the [assembly] line needed that prefixed to the CustomTabbedPageRenderer, thus: [assembly: ExportRenderer(typeof(TabbedPage), typeof( XaBLE1.Droid.CustomTabbedPageRenderer ) )]. Thank you so much! – bobwki Aug 22 '18 at 00:03
1

For disabling tab swipe you can use PlatformConfiguration in your TabbedPage class

public partial class MyTabbedPage : TabbedPage
{
    public MainTabbedPage ()
    {
        InitializeComponent();
        this.On<Xamarin.Forms.PlatformConfiguration.Android>().SetIsSwipePagingEnabled(false);    
    }
}

if you don't have MyTabbedPage class, add it than your axml file structure would look like this

<?xml version="1.0" encoding="utf-8" ?>
<MyTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="XaBLE1.MainPage">   
</MyTabbedPage>
R15
  • 13,982
  • 14
  • 97
  • 173
  • Thank you for this answer, but, unfortunately it has not (yet) worked for me. The code-behind shows SetIsSwipePagingEnabled as not there unless I include using Xamarin.Forms.PlatformConfiguration.AndroidSpecific, which then causes ambiguity on "TabbedPage". I have "fixed" by specifying Xamarin.Forms.TabbedPage, but this has not changed the behavior. – bobwki Aug 16 '18 at 19:17
  • I have tested code above working fine. Did you modify your xaml file? – R15 Aug 17 '18 at 09:25
  • I tried adding the line android:TabbedPage.IsSwipePagingEnabled="False" to the xaml file, but when that didn't work I put the code you described in the code-behind for that file. – bobwki Aug 21 '18 at 23:01
1

It looks like, there is a simpler alternative since Android 9.

From James Matemagno's blog

Daniel
  • 9,312
  • 3
  • 48
  • 48