Skip to content

Instantly share code, notes, and snippets.

@15mgm15
Last active July 31, 2018 07:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 15mgm15/76914bf623e33676252864ac1898e7f9 to your computer and use it in GitHub Desktop.
Save 15mgm15/76914bf623e33676252864ac1898e7f9 to your computer and use it in GitHub Desktop.
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);
}
}
}
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<TextView> _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}");
}
}
}
}
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
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<TextView
android:id="@+id/notifications.badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="2dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:minWidth="20dp"
android:visibility="gone"
android:gravity="center"
android:padding="3dp"
android:textColor="@color/white"
android:textSize="11sp" />
</FrameLayout>
@sthewissen
Copy link

While hiding the tab bar the space it took up remains used and visible. Is this something you also encountered?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment