前言

系列目录

C#使用Xamarin开发可移植移动应用目录

源码地址:https://github.com/l2999019/DemoApp

可以Star一下,随意 - -

说点什么..

本篇..基本可以算是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 = -; 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(, 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(, , 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();
} /// <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开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码的更多相关文章

  1. C#使用Xamarin开发可移植移动应用进阶篇(8.打包生成安卓APK并精简大小),附源码

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 嗯,前面讲 ...

  2. 使用Xamarin开发手机聊天程序 -- 基础篇(大量图文讲解 step by step,附源码下载)

    如果是.NET开发人员,想学习手机应用开发(Android和iOS),Xamarin 无疑是最好的选择,编写一次,即可发布到Android和iOS平台,真是利器中的利器啊!而且,Xamarin已经被微 ...

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

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 本篇..基 ...

  4. C#使用Xamarin开发可移植移动应用进阶篇(9.混淆代码,防止反编译)

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 今天讲讲如 ...

  5. C#使用Xamarin开发可移植移动应用进阶篇(10.综合演练,来一份增删改查CRUD)

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 呃 也有半 ...

  6. 使用Xamarin开发即时通信系统 -- 基础篇(大量图文讲解 step by step,附源码下载)...

    如果是.NET开发人员,想学习手机应用开发(Android和iOS),Xamarin 无疑是最好的选择,编写一次,即可发布到Android和iOS平台,真是利器中的利器啊!而且,Xamarin已经被微 ...

  7. 仿酷狗音乐播放器开发日志二十三 修复Option控件显示状态不全的bug(附源码)

    转载请说明原出处,谢谢~~ 整个仿酷狗工程的开发将近尾声,现在还差选项设置窗体的部分,显然在设置窗体里用的最多的就是OptionUI控件,我在写好大致的布局后去测试效果,发现Option控件的显示效果 ...

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

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 嗯..前面 ...

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

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 嗯..前面 ...

随机推荐

  1. 字符的读写函数:fgetc()和fputc()

    fgetc();    功能:    从文件中读取字符.    头文件:  #include <stdio.h>    函数原型:int fgetc(FILE *stream);    返 ...

  2. data-packed volume container - 每天5分钟玩转 Docker 容器技术(43)

    在上一节的例子中 volume container 的数据归根到底还是在 host 里,有没有办法将数据完全放到 volume container 中,同时又能与其他容器共享呢? 当然可以,通常我们称 ...

  3. 51nod_1714:B君的游戏(博弈 sg打表)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1714 nim游戏的一个变形,需要打出sg函数的表 #incl ...

  4. Jmeter之处理session、cookie以及如何做关联

    具体描述问题之前,我们先了解下session.cookie session.cookie的概念 1.session是放在服务器上的,过期与否取决于服务期的设定,cookie是存在客户端的,过去与否可以 ...

  5. ngin隐藏版本号

    Nginx默认是显示版本号的,如:   这样就给人家看到你的服务器nginx版本,这样暴露出来的版本号就容易变成攻击者可利用的信息.所以,从安全的角度来说,隐藏版本号会相对安全些! 配置如下: 修改n ...

  6. Python开发【笔记】:单线程下执行多个定时任务

    单线程多定时任务 前言:公司业务需求,实例当中大量需要启动定时器的操作:大家都知道python中的定时器用的是threading.Timer,每当启动一个定时器时,程序内部起了一个线程,定时器触发执行 ...

  7. 天地图使用过程中由于display:none导致加载部分地图瓦片失败

    在为按钮添加点击事件让地图显示的时候,初始加载未加载到当前页面尺寸的所有地图瓦片,在display:none之后停止加载地图,所以display:none属性去掉,改为dom解析完成之后$('#map ...

  8. 一步一步学Vue(八)

    本篇完成如下场景: 1.系统包含首页.客户信息查询.登录三个模块 2.默认进入系统首页,如果要进行用户查询,则需要进行登录授权 3.查询用户后点击列表项,则进入详情页面 基于上述场景需求描述,在客户端 ...

  9. 机器学习(3)-Tensorflow安装与测试

    安装.# Ubuntu/Linux 64-bit $ sudo apt-get install python-pip python-dev # Ubuntu/Linux 64-bit, CPU onl ...

  10. 还在为CSS布局发愁?你该看看这7条原则

    一.网页结构分析七条原则 这以下7个原则是经过多年网站实战经验之后的总结,只要掌握这7个原则,可以解决大部分在编写网站布局中的问题. 1.先结构后样式. 2.能用CSS表现出来的效果,就尽量少用图像. ...