7.使用布局渲染器,修改默认布局
2017-09-24

本篇..基本可以算是Xamarin在应用开发过程中的核心了..真的很很很重要..

想学习的..想用的..建议仔细阅读..嗯..打酱油的 ..快速滑倒下面点个推荐 - - 哈哈哈...

今天的学习内容?

也只讲一个,关于Xamarin.Forms针对各个平台如何进行可定制化的布局操作.

也就是针对某个平台的细颗粒化操作.

废话不多说,我们直接开始.

 

正文

嗯..今天我会拿一个项目中的例子出来讲.

说说原因吧,因为在谷歌的安卓开发建议中,是建议类似tab切换操作,是放在顶部的.

然而苹果则不然,他建议放在底部..这样就造成了APP上各个平台对于TabbedPage视图的渲染差别

如图:

虽然在墙外..大多数的APP都遵循了这个规则,然而在我们特色的社会主义新中国..几乎所有的APP都是仿苹果的建议 将Tab标签放到了下面..

嗯,入乡随俗,我们今天就来把这个tab,在安卓中给移到下面.

效果如图吧:

 

既然要移动到下面,那么我们肯定需要重写相关的内容,我们可以找到开源的Xamarin控件BottomNavigationBar

做过安卓的应该都知道,这个是一个安卓中比较流行的控件,嗯..直接被移植到了Xamarin中

我们在安卓的项目下,通过nuget添加这个包如下:

然后我们在可移植的项目中,照常编写我们的TabbedPage页面如下:

<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:local="clr-namespace:Xamarin.FormsDemo_CHN.Views;assembly=Xamarin.FormsDemo_CHN"
            x:Class="Xamarin.FormsDemo_CHN.Views.MainPage" BarBackgroundColor="#7EC0EE" BarTextColor="White">
    <local:ItemsPage Icon="ic_Messaget"/>
    <local:AboutPage Icon="ic_Info"/>
    <local:BaiDuMapPage  Icon="ic_Star" /></TabbedPage>

我们给这个页面取名叫MainPage,后台代码如下:


[XamlCompilation(XamlCompilationOptions.Compile)]    public partial class MainPage : TabbedPage
    {      
       public MainPage()
        {
            InitializeComponent();
        }       
 
        protected override void OnCurrentPageChanged()
        {            base.OnCurrentPageChanged();

            Title = CurrentPage?.Title;
            
        }
    }

啥也不用干,就重写一下页面变更事件,改写一下title而已,很常见的代码.

然后我们回到安卓的项目下.

添加一个类,取名为MainPageRenderer,表示是重新渲染MainPage的

编写渲染特性如下:

[assembly: ExportRenderer(typeof(MainPage), typeof(MainPageRenderer))]
namespace Xamarin.FormsDemo_CHN.Droid
{     
class MainPageRenderer : VisualElementRenderer<MainPage>, IOnTabClickListener

注意,我们这里继承了IOnTabClickListener,这个就是第三方的BottomNavigationBar的事件了,待会我们会用到.

在注意:我们这里因为是重写布局,所以要继承VisualElementRenderer

接下来我们直接上MainPageRenderer 的完整代码,因为内容较多..涉及的方面也比较多.嗯..包含一些安卓方面的重绘之类的.

所以就不一一讲解了.大部分都已经写在了注释当中.请仔细看

class MainPageRenderer : VisualElementRenderer<MainPage>, IOnTabClickListener

    {


        private BottomBar _bottomBar;


        private Page _currentPage;


        private int _lastSelectedTabIndex = -1;


        public MainPageRenderer()

        {

            // Required to say packager to not to add child pages automatically

            AutoPackage = false;

        }


        /// <summary>

        /// 选中后,加载新的页面内容

        /// </summary>

        /// <param name="position"></param>

        public void OnTabSelected(int position)

        {

            LoadPageContent(position);

        }


        public void OnTabReSelected(int position)

        {

        }


        protected override void OnElementChanged(ElementChangedEventArgs<MainPage> e)

        {

            base.OnElementChanged(e);


            if (e.OldElement != null)

            {

                ClearElement(e.OldElement);

            }


            if (e.NewElement != null)

            {

                InitializeElement(e.NewElement);

            }

        }


        protected override void Dispose(bool disposing)

        {

            if (disposing)

            {

                ClearElement(Element);

            }


            base.Dispose(disposing);

        }


        /// <summary>

        /// 重写布局的方法

        /// </summary>

        /// <param name="changed"></param>

        /// <param name="l"></param>

        /// <param name="t"></param>

        /// <param name="r"></param>

        /// <param name="b"></param>

        protected override void OnLayout(bool changed, int l, int t, int r, int b)

        {

            if (Element == null)

            {

                return;

            }


            int width = r - l;

            int height = b - t;


            _bottomBar.Measure(

                MeasureSpec.MakeMeasureSpec(width, MeasureSpecMode.Exactly),

                MeasureSpec.MakeMeasureSpec(height, MeasureSpecMode.AtMost));


            //这里需要重新测量位置和尺寸,为了重新布置tab菜单的位置 

            _bottomBar.Measure(

                MeasureSpec.MakeMeasureSpec(width, MeasureSpecMode.Exactly),

                MeasureSpec.MakeMeasureSpec(_bottomBar.ItemContainer.MeasuredHeight, MeasureSpecMode.Exactly));


            int barHeight = _bottomBar.ItemContainer.MeasuredHeight;


            _bottomBar.Layout(0, b - barHeight, width, b);


            float density = Resources.DisplayMetrics.Density;


            double contentWidthConstraint = width / density;

            double contentHeightConstraint = (height - barHeight) / density;


            if (_currentPage != null)

            {

                var renderer = Platform.GetRenderer(_currentPage);


                renderer.Element.Measure(contentWidthConstraint, contentHeightConstraint);

                renderer.Element.Layout(new Rectangle(0, 0, contentWidthConstraint, contentHeightConstraint));


                renderer.UpdateLayout();

            }

        }


        /// <summary>

        /// 初始化方法

        /// </summary>

        /// <param name="element"></param>

        private void InitializeElement(MainPage element)

        {

            PopulateChildren(element);

        }

        /// <summary>

        /// 生成新的底部控件

        /// </summary>

        /// <param name="element"></param>

        private void PopulateChildren(MainPage element)

        {

            //我们需要删除原有的底部控件,然后添加新的

            _bottomBar?.RemoveFromParent();

            

            _bottomBar = CreateBottomBar(element);

            AddView(_bottomBar);


            LoadPageContent(0);

        }



        /// <summary>

        /// 清除旧的底部控件

        /// </summary>

        /// <param name="element"></param>

        private void ClearElement(MainPage element)

        {

            if (_currentPage != null)

            {

                IVisualElementRenderer renderer = Platform.GetRenderer(_currentPage);


                if (renderer != null)

                {

                    renderer.ViewGroup.RemoveFromParent();

                    renderer.ViewGroup.Dispose();

                    renderer.Dispose();


                    _currentPage = null;

                }


                if (_bottomBar != null)

                {

                    _bottomBar.RemoveFromParent();

                    _bottomBar.Dispose();

                    _bottomBar = null;

                }

            }

        }


        /// <summary>

        /// 创建新的底部控件

        /// </summary>

        /// <param name="element"></param>

        /// <returns></returns>

        private BottomBar CreateBottomBar(MainPage element)

        {

            var bar = new BottomBar(Context);


            // TODO: Configure the bottom bar here according to your needs


            bar.SetOnTabClickListener(this);

            bar.UseFixedMode();


            PopulateBottomBarItems(bar, element.Children);

            var barcolor = element.BarBackgroundColor;

           // Color a = new Color(Convert.ToByte(barcolor.), Convert.ToByte(barcolor.G), Convert.ToByte(barcolor.B), Convert.ToByte(barcolor.A));



            bar.ItemContainer.SetBackgroundColor(barcolor.ToAndroid());

            bar.SetActiveTabColor(Color.White);

            //bar.ItemContainer.

            //bar.ItemContainer.SetBackgroundColor(Color.Red);


            return bar;

        }


        /// <summary>

        /// 查询原来底部的菜单,并添加到新的控件

        /// </summary>

        /// <param name="bar"></param>

        /// <param name="pages"></param>

        private void PopulateBottomBarItems(BottomBar bar, IEnumerable<Page> pages)

        {

            

            var barItems = pages.Select(x => new BottomBarTab(Context.Resources.GetDrawable(x.Icon), x.Title));


            bar.SetItems(barItems.ToArray());

        }


        /// <summary>

        /// 通过选择的下标加载Page

        /// </summary>

        /// <param name="position"></param>

        private void LoadPageContent(int position)

        {

            ShowPage(position);

        }


        /// <summary>

        /// 显示Page的方法

        /// </summary>

        /// <param name="position"></param>

        private void ShowPage(int position)

        {

            if (position != _lastSelectedTabIndex)

            {

                Element.CurrentPage = Element.Children[position];


                if (Element.CurrentPage != null)

                {

                    LoadPageContent(Element.CurrentPage);

                }

            }


            _lastSelectedTabIndex = position;

        }


        /// <summary>

        /// 加载方法

        /// </summary>

        /// <param name="page"></param>

        private void LoadPageContent(Page page)

        {

            UnloadCurrentPage();


            _currentPage = page;


            LoadCurrentPage();


            Element.CurrentPage = _currentPage;

        }


        /// <summary>

        /// 加载当前Page

        /// </summary>

        private void LoadCurrentPage()

        {

            var renderer = Platform.GetRenderer(_currentPage);


            if (renderer == null)

            {

                renderer = Platform.CreateRenderer(_currentPage);

                Platform.SetRenderer(_currentPage, renderer);


               

            }

            else

            {

                var basePage = _currentPage as BaseContentPage;

                basePage?.SendAppearing();

            }

            

            AddView(renderer.ViewGroup);

            renderer.ViewGroup.Visibility = ViewStates.Visible;

          

        }


        /// <summary>

        /// 释放上一个Page

        /// </summary>

        private void UnloadCurrentPage()

        {

            if (_currentPage != null)

            {

                var basePage = _currentPage as BaseContentPage;

                basePage?.SendDisappearing();

                var renderer = Platform.GetRenderer(_currentPage);


                if (renderer != null)

                {

                    renderer.ViewGroup.Visibility = ViewStates.Invisible;

                    RemoveView(renderer.ViewGroup);

                }

                

            }

        }

    }

这样,我们就完成了整个tab菜单的替换工作.当然各位还可以根据需要来直接调用BottomNavigationBar的一些动画效果.其实也是很不错的.

本篇就到此结束了.

相关文章: 

原文地址:http://www.cnblogs.com/GuZhenYin/p/7447336.html