//BottomTabbedPageExtensions.cs
using Xamarin.Forms;
namespace SchedulingTool.Helpers
{
public class BottomTabbedPageExtensions
{
public static readonly BindableProperty TabColorProperty = BindableProperty.CreateAttached(
"TabColor",
typeof(Color),
typeof(BottomTabbedPageExtensions),
Color.Transparent);
public static readonly BindableProperty BadgeCountProperty = BindableProperty.CreateAttached(
"BadgeCount",
typeof(int),
typeof(BottomTabbedPageExtensions),
0);
public static readonly BindableProperty BadgeColorProperty = BindableProperty.CreateAttached(
"BadgeColor",
typeof(Color),
typeof(BottomTabbedPageExtensions),
Colors.OrangeColor);
public static readonly BindableProperty IsTabVisibleProperty = BindableProperty.CreateAttached(
"IsTabVisible", typeof(bool), typeof(BottomTabbedPageExtensions), true);
public static void SetIsTabVisible(BindableObject bindable, bool visible)
{
bindable.SetValue(IsTabVisibleProperty, visible);
}
public static bool GetIsTabVisible(BindableObject bindable)
{
return (bool)bindable.GetValue(IsTabVisibleProperty);
}
public static void SetTabColor(BindableObject bindable, Color color)
{
bindable.SetValue(TabColorProperty, color);
}
public static Color GetTabColor(BindableObject bindable)
{
return (Color)bindable.GetValue(TabColorProperty);
}
public static void SetBadgeCount(BindableObject bindable, int badgeCount)
{
bindable.SetValue(BadgeCountProperty, badgeCount);
}
public static int GetBadgeCount(BindableObject bindable)
{
return (int)bindable.GetValue(BadgeCountProperty);
}
public static void IncreaseBadgeCountBy(BindableObject bindable, int increaseBy)
{
int currentValue = GetBadgeCount(bindable);
if(currentValue == 0 && increaseBy < 0)
{
bindable.SetValue(BadgeCountProperty, 0);
}
if(increaseBy < 0 && (increaseBy > currentValue))
{
bindable.SetValue(BadgeCountProperty, 0);
}
bindable.SetValue(BadgeCountProperty, currentValue + increaseBy);
}
public static void SetBadgeColor(BindableObject bindable, Color color)
{
bindable.SetValue(BadgeColorProperty, color);
}
public static Color GetBadgeColor(BindableObject bindable)
{
return (Color)bindable.GetValue(BadgeColorProperty);
}
}
}
//DroidBottomTabbedPageRenderer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Android.Content;
using Android.Support.Design.Widget;
using Android.Views;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android.AppCompat;
using View = Android.Views.View;
using AndroidRelativeLayout = Android.Widget.RelativeLayout;
using RelativeLayoutParams = Android.Widget.RelativeLayout.LayoutParams;
using Android.Support.Design.Internal;
using Xamarin.Forms.Platform.Android;
using System.ComponentModel;
using Android.Widget;
using Android.Graphics.Drawables;
using Android.Graphics.Drawables.Shapes;
using Android.Util;
using Android.Support.V4.View;
[assembly: ExportRenderer(typeof(ExtendedBottomTabbedPage), typeof(DroidBottomTabbedPageRenderer))]
namespace SchedulingTool.Droid.Renderers
{
public class DroidBottomTabbedPageRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemReselectedListener
{
IDictionary<Page, string> _formsBadges;
List _androidBadges;
bool _isShiftModeSet;
int _l, _t, _r, _b, _width, _height, _tabsHeight;
bool _firstTime;
int _bottomBarHeight;
Context _context;
TabLayout _topBar;
TabbedPage _tabbedPage;
BottomNavigationView _bottomBar;
AndroidRelativeLayout _container;
RelativeLayoutParams _layoutParams;
List<BottomNavigationItemView> _tabBarItems;
ExtendedBottomTabbedPage _extendedTabbedPage;
public DroidBottomTabbedPageRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
_tabbedPage = e.NewElement;
_extendedTabbedPage = (ExtendedBottomTabbedPage)_tabbedPage;
_firstTime = true;
_tabBarItems = new List<BottomNavigationItemView>();
_androidBadges = new List<TextView>();
var children = GetAllChildViews(ViewGroup);
foreach (var bottomNavItemView in children)
{
if (bottomNavItemView is BottomNavigationItemView)
{
var tab = (BottomNavigationItemView)bottomNavItemView;
_tabBarItems.Add(tab);
AddBadge(tab);
}
}
if (children.SingleOrDefault(x => x is BottomNavigationView) is BottomNavigationView bottomNav)
{
_bottomBar = bottomNav;
_bottomBar.SetOnNavigationItemReselectedListener(this);
}
if(children.SingleOrDefault(x => x is AndroidRelativeLayout) is AndroidRelativeLayout container)
{
_container = container;
}
if (children.SingleOrDefault(x => x is TabLayout) is TabLayout topNav)
{
_topBar = topNav;
}
SetTabBadges();
AddPropertyChangedHandlersForPages();
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
try
{
base.OnLayout(changed, l, t, r, b);
_width = r - l;
_height = b - t;
_tabsHeight = Math.Min(_height, Math.Max(_bottomBar.MeasuredHeight, _bottomBar.MinimumHeight));
_l = l;
_t = t;
_r = r;
_b = b;
if (!_isShiftModeSet)
{
_bottomBar.SetShiftMode(false, false);
_isShiftModeSet = true;
}
}
catch (Exception ex)
{
ExceptionHandler.LogException(this, nameof(OnLayout), ex);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(ExtendedBottomTabbedPage.BottomTabBarHidden))
{
HideTabbedPage();
}
}
public async void OnNavigationItemReselected(IMenuItem item)
{
await _extendedTabbedPage.CurrentPage.Navigation.PopToRootAsync();
}
List<View> GetAllChildViews(View view)
{
if (!(view is ViewGroup group))
{
return new List<View> { view };
}
var result = new List<View>();
for (int i = 0; i < group.ChildCount; i++)
{
var child = group.GetChildAt(i);
var childList = new List<View> { child };
childList.AddRange(GetAllChildViews(child));
result.AddRange(childList);
}
return result.Distinct().ToList();
}
void AddPropertyChangedHandlersForPages()
{
foreach (var page in _extendedTabbedPage.Children)
{
page.PropertyChanged += OnPagePropertyChanged;
}
}
void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == BottomTabbedPageExtensions.BadgeCountProperty.PropertyName)
{
var page = (Page)sender;
UpdateBadgeForPage(page);
}
}
void SetTabBadges()
{
var tabCount = _tabbedPage.Children.Count();
_formsBadges = new Dictionary<Page, string>(tabCount);
for (var i = 0; i < tabCount; i++)
{
var page = _tabbedPage.Children[i];
}
}
void AddBadge(BottomNavigationItemView frame)
{
View badge = LayoutInflater.From(_context).Inflate(Resource.Layout.NotificationBadge, frame, false);
frame.AddView(badge);
TextView textViewBadge = (TextView)badge.FindViewById(Resource.Id.notifications_badge);
var backgroundShape = CreateBackgroundShape();
backgroundShape.Paint.Color = Colors.OrangeColor.ToAndroid();
ViewCompat.SetBackground(textViewBadge, backgroundShape);
_androidBadges.Add(textViewBadge);
}
void UpdateBadgeForPage(Page page)
{
if (_tabbedPage == null) return;
var pageIndex = _tabbedPage.Children.IndexOf(page);
var badgeCount = BottomTabbedPageExtensions.GetBadgeCount(page);
if (!_formsBadges.ContainsKey(page))
{
_formsBadges.Add(page, page.Title);
}
var badge = _androidBadges[pageIndex];
var tab = _tabBarItems[pageIndex];
if (badgeCount <= 0)
{
badge.Visibility = ViewStates.Gone;
return;
}
badge.Visibility = ViewStates.Visible;
badge.Text = badgeCount > 99 ? "99+" : badgeCount.ToString();
}
void HideTabbedPage()
{
if (_firstTime)
{
_layoutParams = (RelativeLayoutParams)_bottomBar.LayoutParameters;
_l = _layoutParams.LeftMargin;
_t = _layoutParams.TopMargin;
_r = _layoutParams.RightMargin;
_b = _layoutParams.BottomMargin;
_bottomBarHeight = _layoutParams.Height;
_firstTime = false;
}
if (_extendedTabbedPage.BottomTabBarHidden)
{
_layoutParams.Height = 0;
_bottomBar.LayoutParameters = _layoutParams;
//_topBar.Visibility = ViewStates.Gone;
//_bottomBar.LayoutParameters = new global::Android.Widget.RelativeLayout.LayoutParams(0,0);
//_container.Invalidate();
//_bottomBar.Visibility = ViewStates.Gone;
//Measure(MeasureSpecFactory.MakeMeasureSpec(_width, MeasureSpecMode.Exactly), MeasureSpecFactory.MakeMeasureSpec(_tabsHeight, MeasureSpecMode.Exactly));
//Layout(_l, _t, _r, _b);
}
else
{
_layoutParams.Height = _bottomBarHeight;
_bottomBar.LayoutParameters = _layoutParams;
//_topBar.Visibility = ViewStates.Visible;
//_container.Invalidate();
//_bottomBar.Visibility = ViewStates.Visible;
//Measure(MeasureSpecFactory.MakeMeasureSpec(_width, MeasureSpecMode.Exactly), MeasureSpecFactory.MakeMeasureSpec(_tabsHeight, MeasureSpecMode.Exactly));
//Layout(_l, _t, _r, _b);
}
}
ShapeDrawable CreateBackgroundShape()
{
var radius = DpToPixels(12);
var outerR = new float[] { radius, radius, radius, radius, radius, radius, radius, radius };
return new ShapeDrawable(new RoundRectShape(outerR, null, null));
}
int DpToPixels(float dip)
{
return (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, dip, Resources.DisplayMetrics);
}
}
public static class AndroidHelpers
{
public static void SetShiftMode(this BottomNavigationView bottomNavigationView, bool enableShiftMode, bool enableItemShiftMode)
{
try
{
var menuView = bottomNavigationView.GetChildAt(0) as BottomNavigationMenuView;
if (menuView == null)
{
System.Diagnostics.Debug.WriteLine("Unable to find BottomNavigationMenuView");
return;
}
var shiftMode = menuView.Class.GetDeclaredField("mShiftingMode");
shiftMode.Accessible = true;
shiftMode.SetBoolean(menuView, enableShiftMode);
shiftMode.Accessible = false;
shiftMode.Dispose();
for (int i = 0; i < menuView.ChildCount; i++)
{
var item = menuView.GetChildAt(i) as BottomNavigationItemView;
if (item == null)
continue;
item.SetShiftingMode(enableItemShiftMode);
item.SetChecked(item.ItemData.IsChecked);
}
menuView.UpdateMenuView();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Unable to set shift mode: {ex}");
}
}
}
}
//ExtendedBottomTabbedPage.cs
using Xamarin.Forms;
namespace SchedulingTool.Renderers
{
public class ExtendedBottomTabbedPage : TabbedPage
{
#region Properties & Commands
public static readonly BindableProperty TabBarHiddenProperty =
BindableProperty.Create(nameof(BottomTabBarHidden), typeof(bool), typeof(ExtendedBottomTabbedPage), false);
public bool BottomTabBarHidden
{
get { return (bool)GetValue(TabBarHiddenProperty); }
set { SetValue(TabBarHiddenProperty, value); }
}
public enum BarThemeTypes { Light, DarkWithAlpha, DarkWithoutAlpha }
public BarThemeTypes BarTheme { get; set; }
public bool FixedMode { get; set; }
#endregion
#region Methods
public void RaiseCurrentPageChanged()
{
OnCurrentPageChanged();
}
#endregion
}
}