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,后臺代碼如下:
? ? ??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跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UnitOfWork知多少
- 下一篇: Orleans—一些概念