summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs322
1 files changed, 322 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs
new file mode 100644
index 00000000..f978028b
--- /dev/null
+++ b/Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs
@@ -0,0 +1,322 @@
+using System;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using Android.Content;
+using Android.OS;
+using Android.Runtime;
+using Android.Support.Design.Widget;
+using Android.Support.V4.App;
+using Android.Support.V4.View;
+using Android.Views;
+
+namespace Xamarin.Forms.Platform.Android.AppCompat
+{
+ public class TabbedPageRenderer : VisualElementRenderer<TabbedPage>, TabLayout.IOnTabSelectedListener, ViewPager.IOnPageChangeListener, IManageFragments
+ {
+ bool _disposed;
+ FragmentManager _fragmentManager;
+ TabLayout _tabLayout;
+ bool _useAnimations = true;
+ FormsViewPager _viewPager;
+
+ public TabbedPageRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ public FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = ((FormsAppCompatActivity)Context).SupportFragmentManager);
+
+ internal bool UseAnimations
+ {
+ get { return _useAnimations; }
+ set
+ {
+ FormsViewPager pager = _viewPager;
+
+ _useAnimations = value;
+ if (pager != null)
+ pager.EnableGesture = value;
+ }
+ }
+
+ public void SetFragmentManager(FragmentManager childFragmentManager)
+ {
+ if (_fragmentManager == null)
+ _fragmentManager = childFragmentManager;
+ }
+
+ void ViewPager.IOnPageChangeListener.OnPageScrolled(int position, float positionOffset, int positionOffsetPixels)
+ {
+ UpdateTabBarTranslation(position, positionOffset);
+ }
+
+ void ViewPager.IOnPageChangeListener.OnPageScrollStateChanged(int state)
+ {
+ }
+
+ void ViewPager.IOnPageChangeListener.OnPageSelected(int position)
+ {
+ Element.CurrentPage = Element.Children[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];
+ }
+
+ void TabLayout.IOnTabSelectedListener.OnTabUnselected(TabLayout.Tab tab)
+ {
+ }
+
+ 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.ViewGroup.RemoveFromParent();
+ pageRenderer.Dispose();
+ }
+ pageToRemove.ClearValue(Android.Platform.RendererProperty);
+ }
+
+ if (_viewPager != null)
+ {
+ _viewPager.Adapter.Dispose();
+ _viewPager.Dispose();
+ _viewPager = null;
+ }
+
+ if (_tabLayout != null)
+ {
+ _tabLayout.SetOnTabSelectedListener(null);
+ _tabLayout.Dispose();
+ _tabLayout = null;
+ }
+
+ if (Element != null)
+ Element.InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override void OnAttachedToWindow()
+ {
+ base.OnAttachedToWindow();
+ Element.SendAppearing();
+ }
+
+ protected override void OnDetachedFromWindow()
+ {
+ base.OnDetachedFromWindow();
+ Element.SendDisappearing();
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
+ {
+ base.OnElementChanged(e);
+
+ var activity = (FormsAppCompatActivity)Context;
+
+ if (e.OldElement != null)
+ e.OldElement.InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;
+
+ if (e.NewElement != null)
+ {
+ 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 =
+ new FormsViewPager(activity)
+ {
+ OverScrollMode = OverScrollMode.Never,
+ EnableGesture = UseAnimations,
+ LayoutParameters = new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent),
+ Adapter = new FormsFragmentPagerAdapter<Page>(e.NewElement, FragmentManager) { CountOverride = e.NewElement.Children.Count }
+ };
+ pager.Id = FormsAppCompatActivity.GetUniqueId();
+ pager.AddOnPageChangeListener(this);
+
+ tabs.SetupWithViewPager(pager);
+ UpdateTabIcons();
+ tabs.SetOnTabSelectedListener(this);
+
+ AddView(pager);
+ AddView(tabs);
+ }
+
+ TabbedPage tabbedPage = e.NewElement;
+ if (tabbedPage.CurrentPage != null)
+ ScrollToCurrentPage();
+
+ UpdateIgnoreContainerAreas();
+ tabbedPage.InternalChildren.CollectionChanged += OnChildrenCollectionChanged;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == "CurrentPage")
+ ScrollToCurrentPage();
+ }
+
+ protected override void OnLayout(bool changed, int l, int t, int r, int b)
+ {
+ TabLayout tabs = _tabLayout;
+ FormsViewPager pager = _viewPager;
+ Context context = Context;
+ int width = r - l;
+ int height = b - t;
+
+ tabs.Measure(MeasureSpecFactory.MakeMeasureSpec(width, MeasureSpecMode.Exactly), MeasureSpecFactory.MakeMeasureSpec(height, MeasureSpecMode.AtMost));
+ var tabsHeight = 0;
+
+ //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)
+ {
+ Element.ContainerArea = new Rectangle(0, context.FromPixels(tabsHeight), context.FromPixels(width), context.FromPixels(height - tabsHeight));
+
+ for (var i = 0; i < Element.InternalChildren.Count; i++)
+ {
+ var child = Element.InternalChildren[i] as VisualElement;
+ if (child == null)
+ continue;
+ IVisualElementRenderer renderer = Android.Platform.GetRenderer(child);
+ var navigationRenderer = renderer as NavigationPageRenderer;
+ if (navigationRenderer != null)
+ navigationRenderer.ContainerPadding = tabsHeight;
+ }
+
+ 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)
+ {
+ FormsViewPager pager = _viewPager;
+ TabLayout tabs = _tabLayout;
+
+ ((FormsFragmentPagerAdapter<Page>)pager.Adapter).CountOverride = Element.Children.Count;
+ pager.Adapter.NotifyDataSetChanged();
+
+ if (Element.Children.Count == 0)
+ tabs.RemoveAllTabs();
+ else
+ {
+ tabs.SetupWithViewPager(pager);
+ UpdateTabIcons();
+ tabs.SetOnTabSelectedListener(this);
+ }
+
+ UpdateIgnoreContainerAreas();
+ }
+
+ void ScrollToCurrentPage()
+ {
+ _viewPager.SetCurrentItem(Element.Children.IndexOf(Element.CurrentPage), UseAnimations);
+ }
+
+ void UpdateIgnoreContainerAreas()
+ {
+ foreach (Page child in Element.Children)
+ child.IgnoresContainerArea = child is NavigationPage;
+ }
+
+ void UpdateTabBarTranslation(int position, float offset)
+ {
+ TabLayout tabs = _tabLayout;
+
+ if (position >= Element.InternalChildren.Count)
+ return;
+
+ var leftPage = (Page)Element.InternalChildren[position];
+ IVisualElementRenderer leftRenderer = Android.Platform.GetRenderer(leftPage);
+
+ if (leftRenderer == null)
+ return;
+
+ if (offset <= 0 || position >= Element.InternalChildren.Count - 1)
+ {
+ var leftNavRenderer = leftRenderer as NavigationPageRenderer;
+ if (leftNavRenderer != null)
+ tabs.TranslationY = leftNavRenderer.GetNavBarHeight();
+ else
+ tabs.TranslationY = 0;
+ }
+ else
+ {
+ var rightPage = (Page)Element.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 UpdateTabIcons()
+ {
+ 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);
+ tab.SetIcon(ResourceManager.IdFromTitle(icon, ResourceManager.DrawableClass));
+ }
+ }
+ }
+} \ No newline at end of file