个性化阅读
专注于IT技术分析

如何在Mac上用C#制作Android和iOS应用

本文概述

从前, 有一家公司拥有所有最好的工具, 并且为他们的平台编写软件很棒。但是慢慢地, 他们对自己的问题变得漠不关心。当系统崩溃时, 他们并没有惊慌, 而是将这种宇宙状态视为生命的事实。他们认为自己的程序在自己内部是完美的, 宁静而优雅的, 其目的不言而喻。

哦, 男孩, 如果他们只知道他们有多严重……

当他们意识到自己的错误, 并且首席执行官哭着要带回所有离开平台并扬帆起航的开发人员时, 这早就该到了。公司就是微软, 我坚信他们的命运已经被封印了, 他们一定会在技术领域的最前沿慢慢但必定会灭亡。

我很高兴自己错了!

在过去的几年中, 微软从袖子中抽出了一些王牌。是的, 他们搞砸了Skype(我对此仍然很讨厌), 在智能手机上失败了, 在平板电脑上几乎成功了。但是, 他们也做了一些非常了不起的事情。他们放弃了封闭式帝国的方法, 将.NET开源, 加入Linux基金会, 发布了Linux的SQL Server, 并创建了这个伟大的新工具, 称为Visual Studio for Mac。

没错, 不是Windows而是Mac的真正Microsoft IDE。设想!

适用于Mac的Visual Studio

在Mac上使用C#编写你的第一个跨平台Android和iOS应用程序

你可以使用Visual Studio for Mac创建几乎任何类型的应用程序。它可以是iOS, tvOS, Android, Mac, .NET Core, 甚至是ASP.NET。现在, 所有出色的孩子都在编写移动应用程序, 让我们看看在Visual Studio for Mac中创建可在Android和iOS上运行的C#应用​​程序需要什么。

你需要做的第一件事是选择应用程序模板。让我们从一个简单的” Single View App”开始。

单视图应用程序。

填写软件包名称并引导你的应用程序后, Visual Studio将创建一个包含三个项目的解决方案。第一个项目将是一个共享库, 你应在其中保留平台无关的代码, 另外两个将是Android和iOS应用程序。

入门。

你可以使用”运行”菜单或应用程序栏中的命令来启动你的应用程序。

世界你好,请点击我!
记录两次单击。

恭喜你!现在, 你已经成为iOS和Android开发人员, 无论你之前从未编写过Objective-C, Swift或Java代码行。

不过, 使用我们的应用程序还没有真正完成。让我们让事情变得更有趣, 并整合地图和位置服务。

使用地图和位置服务

请记住, VS for Mac仍处于”预览”中, 使用它并没有太多帮助和文档。有关如何做事的最佳参考仍然是Xamarin官方文档。

Visual Studio For Mac与你在PC上可能看到的Xamarin工具使用的解决方案和应用程序结构不同。在大多数情况下, 你将需要进行实验并解决一些障碍, 以使他们的示例发挥作用。希望微软在发布最终版VS for Mac后能够保持领先地位, 并提供大量MSDN资源。

在iOS上显示当前位置

访问移动设备资源(例如当前位置)要求用户”手动”向你的应用授予使用这些资源的权限。 iOS使用文件info.plist来存储这些设置。 VS for Mac提供了用于编辑此文件的可视界面。我们需要做的第一件事是为名为NSLocationWhenInUseUsageDescription的设置添加一个值。

在使用说明中添加位置值。

注意:设置属性名称时, VS将为” NSLocationWhenInUseUsageDescription”显示一个长名称。这是预期的, 不必担心。

我们的引导应用程序是用一个简单的按钮来计算点击次数的。你要做的第一件事是将其删除并将屏幕内容替换为地图。为此, 请在解决方案浏览器中查找Main.storyboard文件, 然后双击该文件以在编辑器中将其打开。

故事板是应用程序用户界面的直观表示, 显示内容的屏幕以及这些屏幕之间的连接。故事板由一系列场景组成, 每个场景代表一个视图控制器及其视图。场景由segue对象连接, 这些对象代表两个视图控制器之间的过渡。

故事板由Apple引入, 并被Xamarin所采用。有关更多信息, 请参阅Apple文档或Xamarin文档。

删除按钮, 然后将”地图视图”组件添加到页面。

删除地图视图组件。

确保正确命名” mapView”组件。

命名地图视图组件

现在剩下的就是清理ViewController.cs文件, 并修改ViewDidLoad()方法以匹配以下内容:

        using CoreLocation;

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            // Perform any additional setup after loading the view, typically from a nib.

            CLLocationManager locationManager = new CLLocationManager();
            locationManager.RequestWhenInUseAuthorization();
            mapView.ShowsUserLocation = true;
        }

你可以使用”快速修复”功能使VS自动添加对CoreLocation库的引用, 也可以手动添加它。

运行iOS应用后, 你应该会看到访问你的位置的请求。授予权限后, 你的地图会加载一个标准的蓝色圆点, 显示你的位置(或使用iOS模拟器伪装的位置:))。

允许在应用程序中使用位置信息。

在Android上显示当前位置

不幸的是, 谷歌和微软决定使这个简单的任务比iOS复杂一些。为了在Android应用程序中使用地图, 你需要创建Google Maps API密钥, 并将其添加到AndroidManifest.xml文件中。

Xamarin伙计们创建了一个非常简单的指南来获取Google Maps API密钥。在继续之前, 请按照其指南中的步骤进行操作。完成后, 你的AndroidManifest.xml应包含以下设置:

<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR KEY" />

现在, 你可以将地图添加到你的应用程序了。

VS for Mac的伟大之处在于, 它由NuGet提供支持, 就像它的哥哥一样。由于默认情况下不包含地图处理库, 因此你需要安装Xamarin.Forms.Maps软件包。

安装Xamarin.Forms.Maps

但是, 没有”地图视图”组件, 你只需将其拖动到”活动”即可。相反, 向屏幕添加地图需要手动更改Resources-> layout-> Main.axml文件。你可以使用设计器视图删除之前创建的按钮, 然后切换到”代码视图”并在LinearLayout中添加以下片段代码:

    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          class="com.google.android.gms.maps.MapFragment" />

与iOS一样, 你需要配置你的应用以请求适当的权限。为此, 请打开AndroidManifest.xml进行编辑, 然后单击编辑器左下方的”应用程序”按钮。 VS将显示一个用于设置这些值的可视界面。你需要启用其中一些功能, 如下所示。

启用权限。

现在是时候编写一些实际的代码了。找到MainActivity.cs文件, 将其打开以进行编辑, 然后进行以下更改:

添加名称空间引用:

using Android.Gms.Maps.Model;
using Android.Gms.Maps;
using Android.Locations;

Make your MainActivity also a ILocationListener.

public class MainActivity : Activity, ILocationListener

Implement the ILocationListener methods within your MainActivity:
        public void OnProviderEnabled(string provider) {}

        public void OnProviderDisabled(string provider) {}

        public void OnStatusChanged(string provider, Availability status, Bundle extras) {}

        public void OnLocationChanged(Android.Locations.Location location)
        {
            LatLng latLng = new LatLng(location.Latitude, location.Longitude);
            CameraPosition.Builder builder = CameraPosition.InvokeBuilder();
            builder.Target(latLng);
            builder.Zoom(15);
            builder.Bearing(155);
            builder.Tilt(10);
            CameraPosition cameraPosition = builder.Build();
            CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition);

            MapFragment mapFrag = (MapFragment)FragmentManager.FindFragmentById(Resource.Id.map);
            GoogleMap map = mapFrag.Map;
            if (map != null)
            {
                map.MoveCamera(cameraUpdate);
            }
          }

将以下两个变量添加为类级变量:

        LocationManager locMgr;
        string locationProvider;

然后清理OnCreate()方法, 如下所示:

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);


            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            locMgr = GetSystemService(LocationService) as LocationManager;

            Criteria locationCriteria = new Criteria();

            locationCriteria.Accuracy = Accuracy.Coarse;
            locationCriteria.PowerRequirement = Power.Medium;

            locationProvider = locMgr.GetBestProvider(locationCriteria, true);
            locMgr.RequestLocationUpdates(locationProvider, 2000, 1, this);
        }

通过从OnCreate()方法中调用GetSystemService, 你的MainActivity将作为ILocationListener激活, 从而能够处理上面列出的所有事件。

运行你的Android应用程序, 你应该将地图定位到你的位置, 类似于下图。

地图位于萨拉热窝。

在iOS和Android上使用共享库

VS for Mac的最大功能之一就是可以在iOS和Android应用之间共享代码。理想情况下, 我们可以将应用程序的所有业务逻辑都放在一个共享库中, 从而将任何特定于iOS和Android的代码限制为UI的一部分。

让我们创建一个共享类, 该类将异步执行HTTP请求并在Debug控制台中显示内容。

使用以下代码在名为RestClient.cs的共享库中创建一个新的类文件:

(确保使用项目中的正确名称空间)

using System;
using System.Net;

namespace testshared
{
    public delegate void callback(string responseText);

    class ReqState
    {
        public ReqState(HttpWebRequest req, callback cb)
        {
            request = req;
            callback = cb;
        }
        public HttpWebRequest request { get; set; }
        public callback callback;
    }

    public class RestClient
    {
        public RestClient() {}


        public void FetchPage(string url, callback cb)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.BeginGetResponse(new AsyncCallback(FinishWebRequest), new ReqState(request, cb));
        }

        private void FinishWebRequest(IAsyncResult result)
        {
            ReqState reqState = (result.AsyncState as ReqState);
            HttpWebResponse response = reqState.request.EndGetResponse(result) as HttpWebResponse;
            using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
            {
                string responseText = reader.ReadToEnd();
                reqState.callback(responseText);
            }
        }

    }
}

在iOS上使用资料库

在iOS项目中修改ViewController.cs文件以匹配以下代码:

(确保使用项目中的正确名称空间)

using System;
using UIKit;
using System.Diagnostics;

namespace testshared.iOS
{
    public partial class ViewController : UIViewController
    {
        RestClient rest = new RestClient();

        public ViewController(IntPtr handle) : base(handle) {}


        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Perform any additional setup after loading the view, typically from a nib.
            Button.AccessibilityIdentifier = "myButton";
            Button.TouchUpInside += delegate
            {
                Button.SetTitle("Loading...", UIControlState.Normal);
                rest.FetchPage("http://www.google.com", doneCallback);
            };
        }

        public void doneCallback(string content)
        {
            InvokeOnMainThread(() =>
            {
                Debug.Write(content);
                Button.SetTitle("All Done", UIControlState.Normal);
            });
        }

        public override void DidReceiveMemoryWarning()
        {
            base.DidReceiveMemoryWarning();
            // Release any cached data, images, etc that aren't in use.        
        }
    }
}

运行你的iOS应用, 单击按钮, 然后在Visual Studio中选中”应用程序输出”选项卡。它应该显示如下内容:

应用程序输出选项卡。

在Android上使用库

Android应用程式所需的变更与iOS所需的变更非常相似。修改MainActivity.cs文件以匹配以下内容:

(确保使用项目中的正确名称空间)

using Android.App;
using Android.Widget;
using Android.OS;

namespace testshared.Droid
{
    [Activity(Label = "testshared", MainLauncher = true, Icon = "@mipmap/icon")]
    public class MainActivity : Activity
    {
        RestClient rest = new RestClient();

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource, // and attach an event to it
            Button button = FindViewById<Button>(Resource.Id.myButton);

            button.Click += delegate { 
                button.Text = $"Loading...";
                rest.FetchPage("http://www.google.com", doneCallback);
            };
        }

        public void doneCallback(string content)
        {
            RunOnUiThread(() =>
            {
                Button button = FindViewById<Button>(Resource.Id.myButton);
                button.Text = "All done";
                System.Diagnostics.Debug.WriteLine(content);
            });
        }
    }
}

注意:Android和iOS这两个平台的系统架构都要求所有UI交互都在主应用程序线程上进行。这意味着对UI元素的任何更改也应在主线程内进行。这就是RunOnUiThread和InvokeOnMainThread进入的地方。由于HTTP请求是在单独的线程中执行的, 并且doneCallback()在主线程之外被调用, 因此我们必须使用这些方法才能访问按钮并更改标签。

C#开发人员正在接管Android和iOS

Visual Studio for Mac仍然有一些不足之处, 但是从乍一看, 我对它的未来感到非常兴奋。移动应用程序的需求每天都在增长, 并且借助Mac的Visual Studio, Microsoft进一步使一批优秀的C#开发人员可以满足这一需求。

在我们的移动设备开发环境的争夺战中, Swift和Java / JVM现在有了一个非常强大的新竞争者。

相关:.NET Core-走向疯狂和开源。微软, 你花了这么长时间吗?

相关文章:Dart语言:Java和C#不够清晰时

赞(0)
未经允许不得转载:srcmini » 如何在Mac上用C#制作Android和iOS应用

评论 抢沙发

评论前必须登录!