C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码

本篇..基本可以算是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的一些动画效果.其实也是很不错的.

本篇就到此结束了.

相关文章: 

  • C#使用Xamarin开发可移植移动应用(1.入门与Xamarin.Forms页面),附源码

  • .NET Standard@Xamarin.Forms

  • C#使用Xamarin开发可移植移动应用(2.Xamarin.Forms布局,本篇很长,注意)附源码

  • C#使用Xamarin开发可移植移动应用(3.Xamarin.Views控件)附源码

  • C#使用Xamarin开发可移植移动应用(4.进阶篇MVVM双向绑定和命令绑定)附源码

  • C#使用Xamarin开发可移植移动应用(5.进阶篇显示弹出窗口与通讯中心)附源码

  • C#使用Xamarin开发可移植移动应用进阶篇(6.使用渲染器针对单个平台自定义控件),附源码

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


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323879.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汇编语言(十七)之判断三个数是否相等

输入三个数&#xff0c;计算相等数的个数 程序运行&#xff1a; 代码&#xff1a; datas segmenta db 0ffh, 0 ,100 dup(?)b db 0ffh, 0 ,100 dup(?)d db 0ffh, 0 ,100 dup(?) ARRAY dw 1,2,2inputA db input a$inputB db 0dh,0ah,i…

你想象中的实习是什么样的?

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;????雄雄的小课堂????。”今天感谢学长小李同学投稿的文章&#xff0c;在校外实习一年&#xff0c;可谓收获满满&#xff0c;下面就是李心焱同学的实习感悟&#xff0c;请欣赏&#xff01;实习…

汇编语言(十八)之求两个数的最大公约数

输入两个数&#xff0c;使用辗转相除法求最大公约数 程序运行&#xff1a; 代码&#xff1a; datas segmentM_max_len db 0ffhM_len db 0M_string db 100 dup(?)M dw 0 N_max_len db 0ffhN_len db 0N_string db…

Orleans—一些概念

这个文章聊一聊Orleans的概念.以下文章大部分翻译自官方教程,还有一些结合实际的应用经验,并对以前文章留下的坑进行填平.如果有哪个坑没有填,还请告诉我. Grain的生命周期: 一个Grain在逻辑上是永远存在的,并在逻辑上拥有一个不变的标识.程序的代码永远不会去创造或者销毁一个…

使用jdbc连接mysql数据库代码示例

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”最近&#xff0c;正好在看jdbc&#xff0c;于是就整理了一份增删改查的案例&#xff0c;记录记录&#xff0c;以备后用&#xff01;&#xff01;java编辑器&#xff1a;myeclipse![10]…

POJ1958-Strange Towers of Hanoi【递推】

正题 题目链接&#xff1a;http://poj.org/problem?id1958 大意 有4根柱子的汉诺塔&#xff0c;有n个盘&#xff0c;求移动次数 解题思路 首先我们定义只有三根柱子时有n个盘的话移动次数是dndn&#xff0c;然后dndn−1∗21dndn−1∗21定义有四根柱子时有n个盘移动次数为fn…

[上海站] 微软Azure AspNetCore微服务实战

活动介绍 2017官方发布了EshopOnContainers的微服务项目&#xff0c;其结合了.Net Core、Azure、Docker等众多优秀的技术&#xff0c;为开发者们抛砖引玉&#xff0c;走进微服务的大门。 本次分享&#xff0c;我们将结合实际毫无保留的从代码上解析微服务架构&#xff0c;以实…

汇编语言(十九)之删除数组中的元素

在数组中删除所有-1元素&#xff0c;然后输出删除后的长度 程序运行&#xff1a; 代码&#xff1a; datas segmentLink dw 15 ,1,2,3,4,5,6,-9,-1,3,4,5,3,-1,-2,3ouput db Link length:$ datas endsstacks segment stackdb 100h dup(?)stacks endscodes segmentassume cs…

Linux+.NetCore+Nginx搭建集群

本篇和大家分享的是LinuxNetCoreNginx搭建负载集群&#xff0c;对于netcore2.0发布后&#xff0c;我一直在看官网的文档并学习&#xff0c;关注有哪些新增的东西&#xff0c;我&#xff0c;一个从1.0到2.0的跟随者这里只总结一句话&#xff1a;2.0版本&#xff0c;api更多&…

老师 累了,我们 泪了!

“以下文章来自一位不愿意透露姓名的同学所写&#xff1a;”呀呼 “老穆头” 没错是我们的穆老师悄悄往我们班探了探头瞧了瞧我们&#xff0c; 我们依旧对他那么热情&#xff0c;紧随其后老佟往门口一站 教室里立马严肃起来 她是我们级部出了名没人敢惹的老佟&#xff08;佟老…

汇编语言(二十)之分类统计字符个数

输入一串字符串&#xff0c;分别统计英文字符&#xff0c;数字字符和其他字符的个数 程序运行&#xff1a; 代码&#xff1a; datas segmentline_max_length db 0ffhline db 0, 100h dup(?)letter_count dw 0digit_count dw 0other_count …

.NET Core+Selenium+Github+Travis CI =amp;gt; SiteHistory

前言 总是三分钟热度的我折腾了一个可以每天自动截取指定网站页面并保存到Github的项目SiteHistory&#xff0c;感觉挺好(每次都这样 frameborder"0" scrolling"no" style"border-width: initial; border-style: none; width: 25px; height: 26px;&qu…

汇编语言(二十一)之数值交换与自增

输入两个数&#xff0c;如果第一个数为偶数第二个数为奇数&#xff0c;互换位置&#xff1b;如果两个数都是奇数&#xff0c;则两个数都加一 程序运行&#xff1a; 代码&#xff1a; datas segmentA dw 0 B dw 0a_string db 0ffh, 0 ,100h dup(?)b_string …

2021 程序媛跳槽记:学习计划篇(已收获字节等offer)

今天推荐的这篇文章是一位互联网程序媛写的跳槽日记。她本硕毕业于985计算机专业&#xff0c;先后就职于央企和BAT。这一篇就是她跳槽到互联网的学习笔记&#xff0c;希望能对大家有所帮助&#xff0c;感兴趣的读者也可以关注公众号&#xff0c;听听更多程序媛的故事~ 坦白说&a…

Orleans的集群构建

听闻一周前,微软公布了.net core2.0,以及各种各样的其他core2.0.大家都很兴奋.微妈的诚意真是满满的.这次开源势头让我感觉到了微妈的技术实力之雄厚.我在这里祝福C#越来越好.细心的人似乎发现Orleans在github中是和net core分在一起的.Orleans的2.0何时发布呀… 现在我们面对…

汇编语言(二十二)之统计减去奇数的个数

输入一个正数&#xff0c;该数减去递增奇数&#xff08;从1开始&#xff09;直至小于等于零为止&#xff0c;计算该数减去奇数的个数 程序运行&#xff1a; 代码&#xff1a; datas segmentNUM dw 17ANS dw 0NUM_string db 0ffh, 0 ,100 dup(?)inputNUM …

.NET Core 在程序集中集成Razor视图

前言 有时候&#xff0c;我们在开发一个程序集供其他项目引用的时候&#xff0c;可能需要对外输出一些HTML的结构数据。 还有一些情况我们可能开发的是一个中间件&#xff0c;这个中间件需要提供一些界面来对外展示数据或者是内部的一些程序的运行信息&#xff0c;这个时候我…

开学一月,可以自己玩自己做的游戏了!!!

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”《趣味小游戏》的书已经讲完了&#xff0c;国庆假期给同学们布置了小游戏项目的任务&#xff0c;开学时检查了下&#xff0c;大部分做的还是挺不错的。有的同学超常发挥想象&#xff0…

汇编语言(二十三)之求一个数的补数

给定一个数&#xff0c;求该数的补数 程序运行&#xff1a; 代码&#xff1a; datas segmentNUM DB 12H,34H,56H,78H,9AH,0BCH,23H,45HDB 67H,89H,0DEH,13H,24H,35H,46H,57HN DB $-NUMoutputNUM db "orignal data NUM$"outputNUMC d…

ASP.NET Core 防止跨站请求伪造(XSRF\/CSRF)攻击

什么是反伪造攻击? 跨站点请求伪造&#xff08;也称为XSRF或CSRF&#xff0c;发音为see-surf&#xff09;是对Web托管应用程序的攻击&#xff0c;因为恶意网站可能会影响客户端浏览器和浏览器信任网站之间的交互。这种攻击是完全有可能的&#xff0c;因为Web浏览器会自动在每…