Xamarin.Forms – 编写一次,到处运行,而且是本机的?


[原文发表地址] Xamarin.Forms – Write Once, Run Everywhere, AND Be Native?

[原文发表时间] 2014/05/28

1

我在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多种跨平台的控件和布局,在运行时可以映射到本地控件,这意味着你的用户接口完全是本地的。

对于我来说,有趣的是这些“控件/概念”(我的术语)在一个很高的级别被编码,但却被当作本地对应的控件。所以在我2代码中的“选项卡”在移动设备上被描述为最具体的,并且是本地对应的控件,而不是我JAVA例子里的一个普通的选项卡控件。让我们看一个例子。

我的伙伴,James Montemagno,来自Xamarin,一个喜欢辣椒的人,在一个喝了咖啡的深夜,他把最终的跨平台的Hanselman的应用程序放在一起,来对我说明一些观点。这个小的应用程序是用C# 写的,可以运行在本地的Windows Phone,Android和iOS等系统中。它发表在我的博客和tweets上。

image

这是视图之前切换的菜单:

和创建它的代码。为了明确起见,我已经简化了一点,但是想法全是MVVM:

public HomeMasterView(HomeViewModel viewModel)
{
    this.Icon = "slideout.png";
    BindingContext = viewModel;
 
    var layout = new StackLayout { Spacing = 0 };
 
    var label = new ContentView {
        Padding = new Thickness(10, 36, 0, 5),
        BackgroundColor = Color.Transparent,
        Content = new Label {
            Text = "MENU",
            Font = Font.SystemFontOfSize (NamedSize.Medium)
        }
    };
 
    layout.Children.Add(label);
         
    var listView = new ListView ();
 
    var cell = new DataTemplate(typeof(ListImageCell));
 
    cell.SetBinding (TextCell.TextProperty, HomeViewModel.TitlePropertyName);
    cell.SetBinding (ImageCell.ImageSourceProperty, "Icon");
 
    listView.ItemTemplate = cell;
 
    listView.ItemsSource = viewModel.MenuItems;
 
//SNIP
 
    listView.SelectedItem = viewModel.MenuItems[0];
    layout.Children.Add(listView);
 
    Content = layout;
}

这里有几个事情需要注意。看见 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 = "http://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

这里是 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 

http://xamarin.com/forms样本代码 ,还有更多的关于 Xamarin and Xamarin Forms信息。同时你可以在https://github.com/jamesmontemagno/Hanselman.Forms上查看 Hanselman 应用程序的代码。


Comments (0)

Skip to main content