зеркало из https://github.com/DeGsoft/maui-linux.git
951 строка
26 KiB
C#
951 строка
26 KiB
C#
using System;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using Android.Content;
|
|
using Android.Content.Res;
|
|
using Android.Graphics;
|
|
using Android.Graphics.Drawables;
|
|
using Android.OS;
|
|
using Android.Runtime;
|
|
using Android.Support.Design.Widget;
|
|
using Android.Support.V4.App;
|
|
using Android.Support.V4.View;
|
|
using AWidget = Android.Widget;
|
|
using Android.Views;
|
|
using Xamarin.Forms.Internals;
|
|
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
|
|
using ADrawableCompat = Android.Support.V4.Graphics.Drawable.DrawableCompat;
|
|
using AView = Android.Views.View;
|
|
using AMenu = Android.Views.Menu;
|
|
using AColor = Android.Graphics.Color;
|
|
|
|
namespace Xamarin.Forms.Platform.Android.AppCompat
|
|
{
|
|
public class TabbedPageRenderer : VisualElementRenderer<TabbedPage>, TabLayout.IOnTabSelectedListener, ViewPager.IOnPageChangeListener, IManageFragments, BottomNavigationView.IOnNavigationItemSelectedListener
|
|
{
|
|
Drawable _backgroundDrawable;
|
|
Drawable _wrappedBackgroundDrawable;
|
|
ColorStateList _originalTabTextColors;
|
|
ColorStateList _orignalTabIconColors;
|
|
|
|
ColorStateList _newTabTextColors;
|
|
ColorStateList _newTabIconColors;
|
|
|
|
bool _disposed;
|
|
FragmentManager _fragmentManager;
|
|
TabLayout _tabLayout;
|
|
BottomNavigationView _bottomNavigationView;
|
|
AWidget.RelativeLayout _relativeLayout;
|
|
FormsViewPager _viewPager;
|
|
Page _previousPage;
|
|
int[] _checkedStateSet = null;
|
|
int[] _selectedStateSet = null;
|
|
int[] _emptyStateSet = null;
|
|
int _defaultARGBColor = Color.Default.ToAndroid().ToArgb();
|
|
AColor _defaultAndroidColor = Color.Default.ToAndroid();
|
|
Platform _platform;
|
|
|
|
public TabbedPageRenderer(Context context) : base(context)
|
|
{
|
|
AutoPackage = false;
|
|
}
|
|
|
|
[Obsolete("This constructor is obsolete as of version 2.5. Please use TabbedPageRenderer(Context) instead.")]
|
|
public TabbedPageRenderer()
|
|
{
|
|
AutoPackage = false;
|
|
}
|
|
|
|
Platform Platform
|
|
{
|
|
get
|
|
{
|
|
if (_platform == null)
|
|
{
|
|
if (Context is FormsAppCompatActivity activity)
|
|
{
|
|
_platform = activity.Platform;
|
|
}
|
|
}
|
|
|
|
return _platform;
|
|
}
|
|
}
|
|
|
|
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = ((FormsAppCompatActivity)Context).SupportFragmentManager);
|
|
bool IsBottomTabPlacement => (Element != null) ? Element.OnThisPlatform().GetToolbarPlacement() == ToolbarPlacement.Bottom : false;
|
|
public Color BarItemColor => (Element != null) ? Element.OnThisPlatform().GetBarItemColor() : Color.Default;
|
|
public Color BarSelectedItemColor => (Element != null) ? Element.OnThisPlatform().GetBarSelectedItemColor() : Color.Default;
|
|
|
|
IPageController PageController => Element as IPageController;
|
|
|
|
void IManageFragments.SetFragmentManager(FragmentManager childFragmentManager)
|
|
{
|
|
if (_fragmentManager == null)
|
|
_fragmentManager = childFragmentManager;
|
|
}
|
|
|
|
void ViewPager.IOnPageChangeListener.OnPageScrolled(int position, float positionOffset, int positionOffsetPixels)
|
|
{
|
|
if (!IsBottomTabPlacement)
|
|
UpdateTabBarTranslation(position, positionOffset);
|
|
}
|
|
|
|
void ViewPager.IOnPageChangeListener.OnPageScrollStateChanged(int state)
|
|
{
|
|
}
|
|
|
|
void ViewPager.IOnPageChangeListener.OnPageSelected(int position)
|
|
{
|
|
if (_previousPage != Element.CurrentPage)
|
|
{
|
|
_previousPage?.SendDisappearing();
|
|
_previousPage = Element.CurrentPage;
|
|
}
|
|
Element.CurrentPage = Element.Children[position];
|
|
Element.CurrentPage.SendAppearing();
|
|
|
|
if (IsBottomTabPlacement)
|
|
_bottomNavigationView.SelectedItemId = position;
|
|
}
|
|
|
|
void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
|
|
{
|
|
}
|
|
|
|
void TabLayout.IOnTabSelectedListener.OnTabSelected(TabLayout.Tab tab)
|
|
{
|
|
if (Element == null)
|
|
return;
|
|
|
|
int selectedIndex = tab.Position;
|
|
if (Element.Children.Count > selectedIndex && selectedIndex >= 0)
|
|
Element.CurrentPage = Element.Children[selectedIndex];
|
|
|
|
SetIconColorFilter(tab, true);
|
|
}
|
|
|
|
void TabLayout.IOnTabSelectedListener.OnTabUnselected(TabLayout.Tab tab)
|
|
{
|
|
SetIconColorFilter(tab, false);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing && !_disposed)
|
|
{
|
|
_disposed = true;
|
|
RemoveAllViews();
|
|
foreach (Page pageToRemove in Element.Children)
|
|
{
|
|
IVisualElementRenderer pageRenderer = Android.Platform.GetRenderer(pageToRemove);
|
|
if (pageRenderer != null)
|
|
{
|
|
pageRenderer.View.RemoveFromParent();
|
|
pageRenderer.Dispose();
|
|
}
|
|
pageToRemove.PropertyChanged -= OnPagePropertyChanged;
|
|
pageToRemove.ClearValue(Android.Platform.RendererProperty);
|
|
}
|
|
|
|
if (_viewPager != null)
|
|
{
|
|
_viewPager.Adapter.Dispose();
|
|
_viewPager.Dispose();
|
|
_viewPager = null;
|
|
}
|
|
|
|
if (_tabLayout != null)
|
|
{
|
|
_tabLayout.AddOnTabSelectedListener(null);
|
|
_tabLayout.Dispose();
|
|
_tabLayout = null;
|
|
}
|
|
|
|
if (_bottomNavigationView != null)
|
|
{
|
|
_bottomNavigationView.SetOnNavigationItemSelectedListener(null);
|
|
_bottomNavigationView.Dispose();
|
|
_bottomNavigationView = null;
|
|
}
|
|
|
|
if (_relativeLayout != null)
|
|
{
|
|
_relativeLayout.Dispose();
|
|
_relativeLayout = null;
|
|
}
|
|
|
|
if (Element != null)
|
|
PageController.InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;
|
|
|
|
_previousPage = null;
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
protected override void OnAttachedToWindow()
|
|
{
|
|
base.OnAttachedToWindow();
|
|
PageController.SendAppearing();
|
|
}
|
|
|
|
protected override void OnDetachedFromWindow()
|
|
{
|
|
base.OnDetachedFromWindow();
|
|
PageController.SendDisappearing();
|
|
}
|
|
|
|
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
|
|
{
|
|
base.OnElementChanged(e);
|
|
|
|
var activity = (FormsAppCompatActivity)Context;
|
|
|
|
if (e.OldElement != null)
|
|
((IPageController)e.OldElement).InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;
|
|
|
|
if (e.NewElement != null)
|
|
{
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
if (_relativeLayout == null)
|
|
{
|
|
_relativeLayout = new AWidget.RelativeLayout(Context)
|
|
{
|
|
LayoutParameters = new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent),
|
|
};
|
|
|
|
if (_bottomNavigationView != null)
|
|
{
|
|
_relativeLayout.RemoveView(_bottomNavigationView);
|
|
_bottomNavigationView.SetOnNavigationItemSelectedListener(null);
|
|
}
|
|
|
|
var bottomNavigationViewLayoutParams = new AWidget.RelativeLayout.LayoutParams(
|
|
LayoutParams.MatchParent,
|
|
LayoutParams.WrapContent);
|
|
|
|
bottomNavigationViewLayoutParams.AddRule(AWidget.LayoutRules.AlignParentBottom);
|
|
|
|
_bottomNavigationView = new BottomNavigationView(Context)
|
|
{
|
|
LayoutParameters = bottomNavigationViewLayoutParams,
|
|
Id = Platform.GenerateViewId()
|
|
};
|
|
|
|
var viewPagerParams = new AWidget.RelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent);
|
|
viewPagerParams.AddRule(AWidget.LayoutRules.Above, _bottomNavigationView.Id);
|
|
|
|
FormsViewPager pager = _viewPager = CreateFormsViewPager(activity, e.NewElement);
|
|
|
|
pager.Id = Platform.GenerateViewId();
|
|
pager.AddOnPageChangeListener(this);
|
|
|
|
_relativeLayout.AddView(pager, viewPagerParams);
|
|
_relativeLayout.AddView(_bottomNavigationView, bottomNavigationViewLayoutParams);
|
|
|
|
AddView(_relativeLayout);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_tabLayout == null)
|
|
{
|
|
TabLayout tabs;
|
|
if (FormsAppCompatActivity.TabLayoutResource > 0)
|
|
tabs = _tabLayout = activity.LayoutInflater.Inflate(FormsAppCompatActivity.TabLayoutResource, null).JavaCast<TabLayout>();
|
|
else
|
|
tabs = _tabLayout = new TabLayout(activity) { TabMode = TabLayout.ModeFixed, TabGravity = TabLayout.GravityFill };
|
|
|
|
FormsViewPager pager = _viewPager = CreateFormsViewPager(activity, e.NewElement);
|
|
|
|
pager.Id = Platform.GenerateViewId();
|
|
pager.AddOnPageChangeListener(this);
|
|
|
|
AddView(pager);
|
|
AddView(tabs);
|
|
}
|
|
}
|
|
|
|
OnChildrenCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
|
|
TabbedPage tabbedPage = e.NewElement;
|
|
if (tabbedPage.CurrentPage != null)
|
|
ScrollToCurrentPage();
|
|
|
|
_previousPage = tabbedPage.CurrentPage;
|
|
|
|
((IPageController)tabbedPage).InternalChildren.CollectionChanged += OnChildrenCollectionChanged;
|
|
UpdateBarBackgroundColor();
|
|
UpdateBarTextColor();
|
|
UpdateItemIconColor();
|
|
UpdateSwipePaging();
|
|
UpdateOffscreenPageLimit();
|
|
}
|
|
}
|
|
|
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
base.OnElementPropertyChanged(sender, e);
|
|
|
|
if (e.PropertyName == nameof(TabbedPage.CurrentPage))
|
|
{
|
|
if (Element.CurrentPage != null)
|
|
ScrollToCurrentPage();
|
|
}
|
|
else if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
|
|
UpdateBarBackgroundColor();
|
|
else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName ||
|
|
e.PropertyName == PlatformConfiguration.AndroidSpecific.TabbedPage.BarItemColorProperty.PropertyName ||
|
|
e.PropertyName == PlatformConfiguration.AndroidSpecific.TabbedPage.BarSelectedItemColorProperty.PropertyName)
|
|
{
|
|
_newTabTextColors = null;
|
|
_newTabIconColors = null;
|
|
UpdateBarTextColor();
|
|
UpdateItemIconColor();
|
|
}
|
|
else if (e.PropertyName == PlatformConfiguration.AndroidSpecific.TabbedPage.IsSwipePagingEnabledProperty.PropertyName)
|
|
UpdateSwipePaging();
|
|
}
|
|
|
|
void SetNavigationRendererPadding(int paddingTop, int paddingBottom)
|
|
{
|
|
for (var i = 0; i < PageController.InternalChildren.Count; i++)
|
|
{
|
|
var child = PageController.InternalChildren[i] as VisualElement;
|
|
if (child == null)
|
|
continue;
|
|
IVisualElementRenderer renderer = Android.Platform.GetRenderer(child);
|
|
var navigationRenderer = renderer as NavigationPageRenderer;
|
|
if (navigationRenderer != null)
|
|
{
|
|
navigationRenderer.ContainerTopPadding = paddingTop;
|
|
navigationRenderer.ContainerBottomPadding = paddingBottom;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnLayout(bool changed, int l, int t, int r, int b)
|
|
{
|
|
FormsViewPager pager = _viewPager;
|
|
Context context = Context;
|
|
|
|
var width = r - l;
|
|
var height = b - t;
|
|
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
if (width <= 0 || height <= 0)
|
|
return;
|
|
|
|
_relativeLayout.Measure(
|
|
MeasureSpec.MakeMeasureSpec(width, MeasureSpecMode.Exactly),
|
|
MeasureSpec.MakeMeasureSpec(height, MeasureSpecMode.Exactly));
|
|
|
|
pager.Measure(MeasureSpecFactory.MakeMeasureSpec(width, MeasureSpecMode.AtMost), MeasureSpecFactory.MakeMeasureSpec(height, MeasureSpecMode.AtMost));
|
|
|
|
if (width > 0 && height > 0)
|
|
{
|
|
PageController.ContainerArea = new Rectangle(0, 0, context.FromPixels(width), context.FromPixels(height - _bottomNavigationView.MeasuredHeight));
|
|
|
|
SetNavigationRendererPadding(0, _bottomNavigationView.MeasuredHeight);
|
|
|
|
pager.Layout(0, 0, width, b);
|
|
// We need to measure again to ensure that the tabs show up
|
|
_relativeLayout.Measure(
|
|
MeasureSpec.MakeMeasureSpec(width, MeasureSpecMode.Exactly),
|
|
MeasureSpec.MakeMeasureSpec(height, MeasureSpecMode.Exactly));
|
|
_relativeLayout.Layout(0, 0, _relativeLayout.MeasuredWidth, _relativeLayout.MeasuredHeight);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TabLayout tabs = _tabLayout;
|
|
|
|
tabs.Measure(MeasureSpecFactory.MakeMeasureSpec(width, MeasureSpecMode.Exactly), MeasureSpecFactory.MakeMeasureSpec(height, MeasureSpecMode.AtMost));
|
|
var tabsHeight = 0;
|
|
|
|
if (tabs.Visibility != ViewStates.Gone)
|
|
{
|
|
//MinimumHeight is only available on API 16+
|
|
if ((int)Build.VERSION.SdkInt >= 16)
|
|
tabsHeight = Math.Min(height, Math.Max(tabs.MeasuredHeight, tabs.MinimumHeight));
|
|
else
|
|
tabsHeight = Math.Min(height, tabs.MeasuredHeight);
|
|
}
|
|
|
|
pager.Measure(MeasureSpecFactory.MakeMeasureSpec(width, MeasureSpecMode.AtMost), MeasureSpecFactory.MakeMeasureSpec(height, MeasureSpecMode.AtMost));
|
|
|
|
if (width > 0 && height > 0)
|
|
{
|
|
PageController.ContainerArea = new Rectangle(0, context.FromPixels(tabsHeight), context.FromPixels(width), context.FromPixels(height - tabsHeight));
|
|
|
|
SetNavigationRendererPadding(tabsHeight, 0);
|
|
|
|
pager.Layout(0, 0, width, b);
|
|
// We need to measure again to ensure that the tabs show up
|
|
tabs.Measure(MeasureSpecFactory.MakeMeasureSpec(width, MeasureSpecMode.Exactly), MeasureSpecFactory.MakeMeasureSpec(tabsHeight, MeasureSpecMode.Exactly));
|
|
tabs.Layout(0, 0, width, tabsHeight);
|
|
|
|
UpdateTabBarTranslation(pager.CurrentItem, 0);
|
|
}
|
|
}
|
|
|
|
base.OnLayout(changed, l, t, r, b);
|
|
}
|
|
|
|
void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
e.Apply((o, i, c) => SetupPage((Page)o), (o, i) => TeardownPage((Page)o), Reset);
|
|
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
FormsViewPager pager = _viewPager;
|
|
BottomNavigationView bottomNavigationView = _bottomNavigationView;
|
|
|
|
((FormsFragmentPagerAdapter<Page>)pager.Adapter).CountOverride = Element.Children.Count;
|
|
|
|
pager.Adapter.NotifyDataSetChanged();
|
|
|
|
if (Element.Children.Count == 0)
|
|
{
|
|
bottomNavigationView.Menu.Clear();
|
|
}
|
|
else
|
|
{
|
|
SetupBottomNavigationView(e);
|
|
UpdateBottomNavigationViewIcons();
|
|
bottomNavigationView.SetOnNavigationItemSelectedListener(this);
|
|
}
|
|
|
|
UpdateIgnoreContainerAreas();
|
|
}
|
|
else
|
|
{
|
|
FormsViewPager pager = _viewPager;
|
|
TabLayout tabs = _tabLayout;
|
|
|
|
((FormsFragmentPagerAdapter<Page>)pager.Adapter).CountOverride = Element.Children.Count;
|
|
pager.Adapter.NotifyDataSetChanged();
|
|
if (Element.Children.Count == 0)
|
|
{
|
|
tabs.RemoveAllTabs();
|
|
tabs.SetupWithViewPager(null);
|
|
}
|
|
else
|
|
{
|
|
tabs.SetupWithViewPager(pager);
|
|
UpdateTabIcons();
|
|
tabs.AddOnTabSelectedListener(this);
|
|
}
|
|
|
|
UpdateIgnoreContainerAreas();
|
|
}
|
|
}
|
|
|
|
FormsViewPager CreateFormsViewPager(Context context, TabbedPage tabbedPage)
|
|
{
|
|
return new FormsViewPager(context)
|
|
{
|
|
OverScrollMode = OverScrollMode.Never,
|
|
EnableGesture = tabbedPage.OnThisPlatform().IsSwipePagingEnabled(),
|
|
LayoutParameters = new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent),
|
|
Adapter = new FormsFragmentPagerAdapter<Page>(tabbedPage, FragmentManager) { CountOverride = tabbedPage.Children.Count }
|
|
};
|
|
}
|
|
|
|
void TeardownPage(Page page)
|
|
{
|
|
page.PropertyChanged -= OnPagePropertyChanged;
|
|
}
|
|
|
|
void SetupPage(Page page)
|
|
{
|
|
page.PropertyChanged += OnPagePropertyChanged;
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
foreach (var page in Element.Children)
|
|
SetupPage(page);
|
|
}
|
|
|
|
void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == Page.TitleProperty.PropertyName)
|
|
{
|
|
var page = (Page)sender;
|
|
var index = Element.Children.IndexOf(page);
|
|
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
IMenuItem tab = _bottomNavigationView.Menu.GetItem(index);
|
|
tab.SetTitle(page.Title);
|
|
}
|
|
else
|
|
{
|
|
TabLayout.Tab tab = _tabLayout.GetTabAt(index);
|
|
tab.SetText(page.Title);
|
|
}
|
|
}
|
|
else if (e.PropertyName == Page.IconProperty.PropertyName)
|
|
{
|
|
var page = (Page)sender;
|
|
var index = Element.Children.IndexOf(page);
|
|
FileImageSource icon = page.Icon;
|
|
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
var menuItem = _bottomNavigationView.Menu.GetItem(index);
|
|
menuItem.SetIcon(GetIconDrawable(icon));
|
|
}
|
|
else
|
|
{
|
|
TabLayout.Tab tab = _tabLayout.GetTabAt(index);
|
|
SetTabIcon(tab, icon);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScrollToCurrentPage()
|
|
{
|
|
if (Platform != null)
|
|
{
|
|
Platform.NavAnimationInProgress = true;
|
|
}
|
|
|
|
_viewPager.SetCurrentItem(Element.Children.IndexOf(Element.CurrentPage), Element.OnThisPlatform().IsSmoothScrollEnabled());
|
|
|
|
if (Platform != null)
|
|
{
|
|
Platform.NavAnimationInProgress = false;
|
|
}
|
|
}
|
|
|
|
void UpdateIgnoreContainerAreas()
|
|
{
|
|
foreach (IPageController child in Element.Children)
|
|
child.IgnoresContainerArea = child is NavigationPage;
|
|
}
|
|
|
|
void UpdateOffscreenPageLimit()
|
|
{
|
|
_viewPager.OffscreenPageLimit = Element.OnThisPlatform().OffscreenPageLimit();
|
|
}
|
|
|
|
void UpdateSwipePaging()
|
|
{
|
|
_viewPager.EnableGesture = Element.OnThisPlatform().IsSwipePagingEnabled();
|
|
}
|
|
|
|
void UpdateTabBarTranslation(int position, float offset)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
TabLayout tabs = _tabLayout;
|
|
|
|
if (position >= PageController.InternalChildren.Count)
|
|
return;
|
|
|
|
var leftPage = (Page)PageController.InternalChildren[position];
|
|
IVisualElementRenderer leftRenderer = Android.Platform.GetRenderer(leftPage);
|
|
|
|
if (leftRenderer == null)
|
|
return;
|
|
|
|
if (offset <= 0 || position >= PageController.InternalChildren.Count - 1)
|
|
{
|
|
var leftNavRenderer = leftRenderer as NavigationPageRenderer;
|
|
if (leftNavRenderer != null)
|
|
tabs.TranslationY = leftNavRenderer.GetNavBarHeight();
|
|
else
|
|
tabs.TranslationY = 0;
|
|
}
|
|
else
|
|
{
|
|
var rightPage = (Page)PageController.InternalChildren[position + 1];
|
|
IVisualElementRenderer rightRenderer = Android.Platform.GetRenderer(rightPage);
|
|
|
|
var leftHeight = 0;
|
|
var leftNavRenderer = leftRenderer as NavigationPageRenderer;
|
|
if (leftNavRenderer != null)
|
|
leftHeight = leftNavRenderer.GetNavBarHeight();
|
|
|
|
var rightHeight = 0;
|
|
var rightNavRenderer = rightRenderer as NavigationPageRenderer;
|
|
if (rightNavRenderer != null)
|
|
rightHeight = rightNavRenderer.GetNavBarHeight();
|
|
|
|
tabs.TranslationY = leftHeight + (rightHeight - leftHeight) * offset;
|
|
}
|
|
}
|
|
|
|
void SetupBottomNavigationView(NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
BottomNavigationView bottomNavigationView = _bottomNavigationView;
|
|
|
|
int startingIndex = 0;
|
|
|
|
if (e.Action == NotifyCollectionChangedAction.Add && e.NewStartingIndex == bottomNavigationView.Menu.Size())
|
|
startingIndex = e.NewStartingIndex;
|
|
else if (e.Action == NotifyCollectionChangedAction.Remove && (e.OldStartingIndex + 1) == bottomNavigationView.Menu.Size())
|
|
{
|
|
startingIndex = Element.Children.Count;
|
|
bottomNavigationView.Menu.RemoveItem(e.OldStartingIndex);
|
|
}
|
|
else
|
|
bottomNavigationView.Menu.Clear();
|
|
|
|
|
|
for (var i = startingIndex; i < Element.Children.Count; i++)
|
|
{
|
|
Page child = Element.Children[i];
|
|
var menuItem = bottomNavigationView.Menu.Add(AMenu.None, i, i, child.Title);
|
|
if (Element.CurrentPage == child)
|
|
bottomNavigationView.SelectedItemId = menuItem.ItemId;
|
|
}
|
|
|
|
if (Element.CurrentPage == null && Element.Children.Count > 0)
|
|
Element.CurrentPage = Element.Children[0];
|
|
}
|
|
|
|
void UpdateBottomNavigationViewIcons()
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
BottomNavigationView bottomNavigationView = _bottomNavigationView;
|
|
|
|
for (var i = 0; i < Element.Children.Count; i++)
|
|
{
|
|
Page child = Element.Children[i];
|
|
FileImageSource icon = child.Icon;
|
|
if (string.IsNullOrEmpty(icon))
|
|
continue;
|
|
|
|
var menuItem = bottomNavigationView.Menu.GetItem(i);
|
|
menuItem.SetIcon(GetIconDrawable(icon));
|
|
}
|
|
}
|
|
|
|
void UpdateTabIcons()
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
TabLayout tabs = _tabLayout;
|
|
|
|
if (tabs.TabCount != Element.Children.Count)
|
|
return;
|
|
|
|
for (var i = 0; i < Element.Children.Count; i++)
|
|
{
|
|
Page child = Element.Children[i];
|
|
FileImageSource icon = child.Icon;
|
|
if (string.IsNullOrEmpty(icon))
|
|
continue;
|
|
|
|
TabLayout.Tab tab = tabs.GetTabAt(i);
|
|
SetTabIcon(tab, icon);
|
|
}
|
|
}
|
|
|
|
protected virtual Drawable GetIconDrawable(FileImageSource icon) =>
|
|
Context.GetDrawable(icon);
|
|
|
|
protected virtual void SetTabIcon(TabLayout.Tab tab, FileImageSource icon)
|
|
{
|
|
tab.SetIcon(GetIconDrawable(icon));
|
|
this.SetIconColorFilter(tab);
|
|
}
|
|
|
|
void UpdateBarBackgroundColor()
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
Color tintColor = Element.BarBackgroundColor;
|
|
|
|
if (tintColor.IsDefault)
|
|
_bottomNavigationView.SetBackground(null);
|
|
else if (!tintColor.IsDefault)
|
|
_bottomNavigationView.SetBackgroundColor(tintColor.ToAndroid());
|
|
}
|
|
else
|
|
{
|
|
Color tintColor = Element.BarBackgroundColor;
|
|
|
|
if (Forms.IsLollipopOrNewer)
|
|
{
|
|
if (tintColor.IsDefault)
|
|
_tabLayout.BackgroundTintMode = null;
|
|
else
|
|
{
|
|
_tabLayout.BackgroundTintMode = PorterDuff.Mode.Src;
|
|
_tabLayout.BackgroundTintList = ColorStateList.ValueOf(tintColor.ToAndroid());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tintColor.IsDefault && _backgroundDrawable != null)
|
|
_tabLayout.SetBackground(_backgroundDrawable);
|
|
else if (!tintColor.IsDefault)
|
|
{
|
|
// if you don't create a new drawable then SetBackgroundColor
|
|
// just sets the color on the background drawable that's saved
|
|
// it doesn't create a new one
|
|
if (_backgroundDrawable == null && _tabLayout.Background != null)
|
|
{
|
|
_backgroundDrawable = _tabLayout.Background;
|
|
_wrappedBackgroundDrawable = ADrawableCompat.Wrap(_tabLayout.Background).Mutate();
|
|
}
|
|
|
|
if (_wrappedBackgroundDrawable != null)
|
|
_tabLayout.Background = _wrappedBackgroundDrawable;
|
|
|
|
_tabLayout.SetBackgroundColor(tintColor.ToAndroid());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual ColorStateList GetItemTextColorStates()
|
|
{
|
|
if (IsDisposed)
|
|
return null;
|
|
|
|
if (_originalTabTextColors == null)
|
|
_originalTabTextColors = (IsBottomTabPlacement) ? _bottomNavigationView.ItemTextColor : _tabLayout.TabTextColors;
|
|
|
|
Color barItemColor = BarItemColor;
|
|
Color barTextColor = Element.BarTextColor;
|
|
Color barSelectedItemColor = BarSelectedItemColor;
|
|
|
|
if (barItemColor.IsDefault && barTextColor.IsDefault && barSelectedItemColor.IsDefault)
|
|
return _originalTabTextColors;
|
|
|
|
if (_newTabTextColors != null)
|
|
return _newTabTextColors;
|
|
|
|
int checkedColor;
|
|
int defaultColor;
|
|
|
|
if (!barTextColor.IsDefault)
|
|
{
|
|
checkedColor = barTextColor.ToAndroid().ToArgb();
|
|
defaultColor = checkedColor;
|
|
}
|
|
else
|
|
{
|
|
defaultColor = barItemColor.ToAndroid().ToArgb();
|
|
|
|
if (barItemColor.IsDefault && _originalTabTextColors != null)
|
|
defaultColor = _originalTabTextColors.DefaultColor;
|
|
|
|
checkedColor = defaultColor;
|
|
|
|
if (!barSelectedItemColor.IsDefault)
|
|
checkedColor = barSelectedItemColor.ToAndroid().ToArgb();
|
|
}
|
|
|
|
_newTabTextColors = GetColorStateList(defaultColor, checkedColor);
|
|
return _newTabTextColors;
|
|
}
|
|
|
|
protected virtual ColorStateList GetItemIconTintColorState()
|
|
{
|
|
if (IsDisposed)
|
|
return null;
|
|
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
if (_orignalTabIconColors == null)
|
|
_orignalTabIconColors = _bottomNavigationView.ItemIconTintList;
|
|
}
|
|
// this ensures that existing behavior doesn't change
|
|
else if (!IsBottomTabPlacement && BarSelectedItemColor.IsDefault && BarItemColor.IsDefault)
|
|
return null;
|
|
|
|
Color barItemColor = BarItemColor;
|
|
Color barSelectedItemColor = BarSelectedItemColor;
|
|
|
|
if (barItemColor.IsDefault && barSelectedItemColor.IsDefault)
|
|
return _orignalTabIconColors;
|
|
|
|
if (_newTabIconColors != null)
|
|
return _newTabIconColors;
|
|
|
|
int defaultColor = barItemColor.ToAndroid().ToArgb();
|
|
|
|
if (barItemColor.IsDefault && _orignalTabIconColors != null)
|
|
defaultColor = _orignalTabIconColors.DefaultColor;
|
|
|
|
int checkedColor = defaultColor;
|
|
|
|
if (!barSelectedItemColor.IsDefault)
|
|
checkedColor = barSelectedItemColor.ToAndroid().ToArgb();
|
|
|
|
_newTabIconColors = GetColorStateList(defaultColor, checkedColor);
|
|
return _newTabIconColors;
|
|
}
|
|
|
|
public bool OnNavigationItemSelected(IMenuItem item)
|
|
{
|
|
if (Element == null || IsDisposed)
|
|
return false;
|
|
|
|
int selectedIndex = item.Order;
|
|
if (_bottomNavigationView.SelectedItemId != item.ItemId && Element.Children.Count > selectedIndex && selectedIndex >= 0)
|
|
Element.CurrentPage = Element.Children[selectedIndex];
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsDisposed
|
|
{
|
|
get
|
|
{
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
if (_disposed || _relativeLayout == null || _bottomNavigationView == null)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (_disposed || _tabLayout == null)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void UpdateItemIconColor()
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
if (IsBottomTabPlacement)
|
|
_bottomNavigationView.ItemIconTintList = GetItemIconTintColorState() ?? _orignalTabIconColors;
|
|
else
|
|
{
|
|
var colors = GetItemIconTintColorState() ?? _orignalTabIconColors;
|
|
for (int i = 0; i < _tabLayout.TabCount; i++)
|
|
{
|
|
TabLayout.Tab tab = _tabLayout.GetTabAt(i);
|
|
this.SetIconColorFilter(tab);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateBarTextColor()
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
var colors = GetItemTextColorStates() ?? _originalTabTextColors;
|
|
if (IsBottomTabPlacement)
|
|
_bottomNavigationView.ItemTextColor = colors;
|
|
else
|
|
_tabLayout.TabTextColors = colors;
|
|
}
|
|
|
|
void SetIconColorFilter(TabLayout.Tab tab)
|
|
{
|
|
SetIconColorFilter(tab, _tabLayout.GetTabAt(_tabLayout.SelectedTabPosition) == tab);
|
|
}
|
|
|
|
void SetIconColorFilter(TabLayout.Tab tab, bool selected)
|
|
{
|
|
var icon = tab.Icon;
|
|
if (icon == null)
|
|
return;
|
|
|
|
var colors = GetItemIconTintColorState();
|
|
if (colors == null)
|
|
ADrawableCompat.SetTintList(icon, null);
|
|
else
|
|
{
|
|
int[] _stateSet = null;
|
|
|
|
if (selected)
|
|
_stateSet = GetSelectedStateSet();
|
|
else
|
|
_stateSet = GetEmptyStateSet();
|
|
|
|
if (colors.GetColorForState(_stateSet, _defaultAndroidColor) == _defaultARGBColor)
|
|
ADrawableCompat.SetTintList(icon, null);
|
|
else
|
|
{
|
|
var wrappedIcon = ADrawableCompat.Wrap(icon);
|
|
if (wrappedIcon != icon)
|
|
{
|
|
icon = wrappedIcon;
|
|
tab.SetIcon(wrappedIcon);
|
|
}
|
|
|
|
icon.Mutate();
|
|
icon.SetState(_stateSet);
|
|
ADrawableCompat.SetTintList(icon, colors);
|
|
}
|
|
}
|
|
icon.InvalidateSelf();
|
|
}
|
|
|
|
int[] GetSelectedStateSet()
|
|
{
|
|
if (IsBottomTabPlacement)
|
|
{
|
|
if (_checkedStateSet == null)
|
|
_checkedStateSet = new int[] { global::Android.Resource.Attribute.StateChecked };
|
|
|
|
return _checkedStateSet;
|
|
}
|
|
else
|
|
{
|
|
if (_selectedStateSet == null)
|
|
_selectedStateSet = GetStateSet(AView.SelectedStateSet);
|
|
|
|
return _selectedStateSet;
|
|
}
|
|
}
|
|
|
|
int[] GetEmptyStateSet()
|
|
{
|
|
if (_emptyStateSet == null)
|
|
_emptyStateSet = GetStateSet(AView.EmptyStateSet);
|
|
|
|
return _emptyStateSet;
|
|
}
|
|
|
|
int[] GetStateSet(System.Collections.Generic.IList<int> stateSet)
|
|
{
|
|
var results = new int[stateSet.Count];
|
|
for (int i = 0; i < results.Length; i++)
|
|
results[i] = stateSet[i];
|
|
|
|
return results;
|
|
}
|
|
|
|
ColorStateList GetColorStateList(int defaultColor, int checkedColor)
|
|
{
|
|
int[][] states = new int[2][];
|
|
int[] colors = new int[2];
|
|
|
|
states[0] = GetSelectedStateSet();
|
|
colors[0] = checkedColor;
|
|
states[1] = GetEmptyStateSet();
|
|
colors[1] = defaultColor;
|
|
|
|
return new ColorStateList(states, colors);
|
|
}
|
|
}
|
|
}
|