diff options
Diffstat (limited to 'Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs')
-rw-r--r-- | Xamarin.Forms.Platform.Android/AppCompat/TabbedPageRenderer.cs | 322 |
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 |