Last active
July 31, 2018 07:26
-
-
Save 15mgm15/76914bf623e33676252864ac1898e7f9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}"); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
While hiding the tab bar the space it took up remains used and visible. Is this something you also encountered?