[原文发表地址] Xamarin.Forms - Write Once, Run Everywhere, AND Be Native?
[原文发表时间] 2014/05/28
![1 1](https://msdntnarchive.z22.web.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/22/metablogapi/3718.1_thumb_47C8DAB8.png)
我在Nike工作的很多年,使用java编写了一个可以在四种平台上运行的订单管理应用程序。我们曾经经常开玩笑说:“编写一次,到处调试”。这是早期的java, 但现在的事实是,每一个窗体和控件都是“自绘的”,这意味着一个按钮在所有的地方看起来都是一样的,因为它不是一个真实的按钮,当然这会因为操作系统而显示不一样。它只是一个按钮的图片。我们经常使用Spy++和不同的Windows检查程序来探索我们的应用程序,并且他们永远看不到一个java程序的控件。这意味着应用程序在任何地方都能工作的很好,而且总是看起来像是一个java应用程序。它们没有和底层平台整合。
使用MVVM(模型,视图,视图-模型)模式,以及在Windows Phone 8.1和windows 8.1上工作的通用应用程序技术,对于某些类型应用程序,代码共享最高可以达到90%。然而,即使一个简单的应用程序,针对每一个平台,你仍然得创建一个自定义的本机视图。大多数情况下,这是可取的,但是对于某些应用程序,它很让人厌烦的,很容易出错,而且很冗长。
Xamarin今天宣布了Xamarin.Forms,我认为它有效地
把本地控件抽象到一个更高级别的概念。过去,在我的眼里,这非常类似于我当年在java中写的代码-所有的都是以布局和流程背后的流利代码完成的。你创建一个控件树。
Xamarin.Forms是一个新的类库,对于iOS, Android和windows phone, 你都可以从一个单一的,共享的C# 代码库里生成本地的UIs。它提供了400多种跨平台的控件和布局,在运行时可以映射到本地控件,这意味着你的用户接口完全是本地的。
对于我来说,有趣的是这些“控件/概念”(我的术语)在一个很高的级别被编码,但却被当作本地对应的控件。所以在我 代码中的“选项卡”在移动设备上被描述为最具体的,并且是本地对应的控件,而不是我JAVA例子里的一个普通的选项卡控件。让我们看一个例子。
我的伙伴,James Montemagno,来自Xamarin,一个喜欢辣椒的人,在一个喝了咖啡的深夜,他把最终的跨平台的Hanselman的应用程序放在一起,来对我说明一些观点。这个小的应用程序是用C# 写的,可以运行在本地的Windows Phone,Android和iOS等系统中。它发表在我的博客和tweets上。
![image image](https://msdntnarchive.z22.web.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/22/metablogapi/8535.image_thumb_60587808.png)
这是视图之前切换的菜单:
和创建它的代码。为了明确起见,我已经简化了一点,但是想法全是MVVM:
这里有几个事情需要注意。看见 ListImageCell了吗? 它是 ImageCell的子类,这是个带有图片的 TextCell,并且可以给文本和图片设置数据绑定。普遍认为每种平台都有文本和图片,但是资源在每一个平台上是不一样的。这是为什么博客和twitter的图片是独一无二的对于各自的平台。概念是共享的,而且是本机实现的,并且看起来是本机的。
那是在UI方面,在逻辑方面,所有加载RSS feed 的代码和Tweets都是在3个平台上互相共享的。对于非阻塞式 I/O,它可以使用异步和等待,而且在twiiter的例子中,它使用了 LinqToTwitter作为一个PCL(便携式类库),这是非常酷的。对于RSS解析,它使用Ling to XML.
private async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
try{
var httpClient = new HttpClient();
var feed = "https://feeds.hanselman.com/ScottHanselman";
var responseString = await httpClient.GetStringAsync(feed);
FeedItems.Clear();
var items = await ParseFeed(responseString);
foreach (var item in items)
{
FeedItems.Add(item);
}
} catch (Exception ex) {
var page = new ContentPage();
var result = page.DisplayAlert ("Error", "Unable to load blog.", "OK", null);
}
IsBusy = false;
}
|
And ParseFeed:
private async Task<List<FeedItem>> ParseFeed(string rss)
{
return await Task.Run(() =>
{
var xdoc = XDocument.Parse(rss);
var id = 0;
return (from item in xdoc.Descendants("item")
select new FeedItem
{
Title = (string)item.Element("title"),
Description = (string)item.Element("description"),
Link = (string)item.Element("link"),
PublishDate = (string)item.Element("pubDate"),
Category = (string)item.Element("category"),
Id = id++
}).ToList();
});
}
|
再一次,共享所有。当到了 Windows Phone, Android, 和 iPhone列表输出数据的时候,在每一种平台上,它看起来都非常棒(读:本机的),它实际上没有做任何事情在具体的平台上。控件看起来是本机的,因为他们是本机的。 Xamarin.Forms 控件是本机控件的封装,它们本身不是一个新的控件。
![image image](https://msdntnarchive.z22.web.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/22/metablogapi/7633.image_thumb_0+D82.png)
这里是 BlogView, Xamarin.Forms中像 ActivityIndicator一样的东西,它表现得像是一个本机的控件。
public BlogView ()
{
BindingContext = new BlogFeedViewModel ();
var refresh = new ToolbarItem {
Command = ViewModel.LoadItemsCommand,
Icon = "refresh.png",
Name = "refresh",
Priority = 0
};
ToolbarItems.Add (refresh);
var stack = new StackLayout {
Orientation = StackOrientation.Vertical,
Padding = new Thickness(0, 8, 0, 8)
};
var activity = new ActivityIndicator {
Color = Helpers.Color.DarkBlue.ToFormsColor(),
IsEnabled = true
};
activity.SetBinding (ActivityIndicator.IsVisibleProperty, "IsBusy");
activity.SetBinding (ActivityIndicator.IsRunningProperty, "IsBusy");
stack.Children.Add (activity);
var listView = new ListView ();
listView.ItemsSource = ViewModel.FeedItems;
var cell = new DataTemplate(typeof(ListTextCell));
cell.SetBinding (TextCell.TextProperty, "Title");
cell.SetBinding (TextCell.DetailProperty, "PublishDate");
cell.SetValue(TextCell.StyleProperty, TextCellStyle.Vertical);
listView.ItemTapped += (sender, args) => {
if(listView.SelectedItem == null)
return;
this.Navigation.PushAsync(new BlogDetailsView(listView.SelectedItem as FeedItem));
listView.SelectedItem = null;
};
listView.ItemTemplate = cell;
stack.Children.Add (listView);
Content = stack;
}
Xamarin Forms是一个非常灵敏的解决方案,有人可能会说,是优雅的,对于编写一次,到处运行,并且不会有麻烦。好的是当你想要关注底层的时候,你可以关心底层平台,当你不想关注的时候,就可以忽略它。一个隐藏了本机平台的解决方案不是本机,是吗?这将是一共同标准最低的解决方案。这个出现是为了隐藏跨平台和多设备程序的冗长和重复。
![image image](https://msdntnarchive.z22.web.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/22/metablogapi/8055.image_thumb_69480A47.png)
|