2018 年 2 月

第 33 卷,第 2 期

本文章是由機器翻譯。

C# - 使用可自訂的指令碼語言撰寫原生行動應用程式

Vassili Kaplan

我可以在 MSDN Magazine 2016 年 2 月問題,說明如何建立剖析 C# 中的數學運算式分割合併演算法為基礎的自訂指令碼語言 (msdn.com/magazine/mt632273)。我呼叫我可以自訂指令碼的 C# 或 CSCS 的語言。最近發佈電子書,提供更多詳細建立自訂的語言 (bit.ly/2yijCod)。建立您自己的指令碼語言可能一開始似乎不是特別有用,即使它 (例如,遊戲作弊) 的一些有趣的應用程式。我也會發現 Unity 程式設計中的某些應用程式。

但是我所發現的可自訂的指令碼語言的應用程式更有趣的然後 — 撰寫跨平台行動裝置應用程式。事實上,您可使用 CSCS 撰寫適用於 Android 和 iOS 的應用程式 (和 Windows Phone 可以輕鬆地新增,以及)。相同的程式碼可以用於所有平台。發佈簡介中的程式碼 Magazine 年 11 月年 12 月 2017年問題該怎麼做 (codemag.com/article/1711081)。

本文章中我們即將深入剖析,並示範如何使用 CSCS 撰寫適用於行動裝置。我也會修正程式碼 Magazine 文件中的某些不準確。您會看到可在原生的平台上執行的任何項目可以完成 CSCS 中。我也將顯示如何將遺失的功能加入 CSCS 即時。

若要執行本文中所顯示的程式碼,您將需要 Visual Studio 2017 安裝,Windows 或 macOS xamarin。個人使用 Visual Studio Community Edition 2017 我 MacBook 上。請注意,若要部署 Apple App store 的 iOS 應用程式需要的 Mac。

"Hello,World !"的行動應用程式

看看圖 1,其中顯示的文字轉換語音和語音辨識某些基本 CSCS 程式碼。讓我們來檢查逐行程式碼。

圖 1"Hello World !"中 CSCS 行動裝置應用程式

AutoScale();
voice = "en-US";
locButtonTalk = GetLocation("ROOT", "CENTER", "ROOT", "BOTTOM", 0, 0);
AddButton(locButtonTalk, "buttonTalk", "Click me", 200, 80);
AddAction(buttonTalk,  "talk_click");
function talk_click(sender, arg) {
  ShowToast("Please say your name...");
  VoiceRecognition("voice_recog", voice);
}
function voice_recog(errorStatus, recognized) {
  if (errorStatus != "") {
    AlertDialog("CSCS", "Error: " + errorStatus);
  } else {
    ShowToast("Word recognized: " + recognized);
    Speak("Hello, " + recognized, voice);
  }
}

自動調整規模函式可讓您自動調整 widget 大小根據實際裝置螢幕大小。比方說,自動調整,widget 會像兩次 1280年像素寬的裝置上的其中一個 640 像素寬度一樣大。自動調整規模函式的實際的簽章是:

AutoScale(scale = 1.0);

如果您不使用預設小數位數 = 1.0 參數,為指定的小數位數將會套用到的差異。例如,如果小數位數 = 的 0.5 時從 640 移至 1280年像素將不會兩次,但 1.5 倍的公式來計算新的大小,所以,調整大小 widget 的差異:

newSize = size + size * scale * (1280 / 640 - 1) = size * (1 + scale) = size * 1.5.

但是如果調整 = 2,widget 會根據計算大 3 次。小數位數的特殊案例 = 0 也符合以下公式:將執行無縮放調整,widget 會有完全相同的大小而不管裝置大小。此小數位數參數也可以套用每個 widget,它可以指定為 GetLocation 函式中的選擇性參數。我將示範如何執行此動作中的位元。

接下來,定義語音變數。請注意,CSCS 類似的 Python 指令碼語言,從內容,會推斷變數的類型,所以語音變數會表示為 C# 字串背後的運作原理。

然後定義一個按鈕。CSCS 中的 widget 定義一律會採用兩個陳述式:第一個指定的小工具的位置,而且第二個 widget 的實際定義。在幕後 UIButton widget 適用於 iOS 和按鈕適用於 Android。

建立位置在螢幕上的一般語法是:

GetLocation(ReferenceX, PlacementX, ReferenceY, PlacementY,
            AdjustmentX = 0, AdjustmentY = 0,
            ScaleOption = false, Scale = 0.0, Parent = null);

以下是引數的意義:

  • ReferenceX:水平位置的另一個小工具名稱。它可以是字串 「 根,」 表示父 widget 或主畫面。
  • PlacementX:相對於小工具的水平點 ReferenceX 所示。這些引數的結尾,會列出可能值。
  • ReferenceY:垂直放置其他小工具名稱。它可以是字串 「 根,」 表示父 widget 或主畫面。
  • PlacementY:相對於 widget 的垂直點 ReferenceY 所示。可能的值會列於結尾這些引數。
  • AdjustmenX:其他的水平移動像素為單位的 widget。它也可以是負數。正的方向是從左到右。
  • AdjustmenY:其他的垂直移動像素為單位的 widget。它也可以是負數。正的方向會從上到下。
  • ScaleOption:指出是否要將特定的縮放選項套用到此 widget。如果這個選項是 false,或未提供,自動調整規模函式中指定的調整將會完成。如果此選項為提供根據小數位數參數,則會修改調整參數和 widget 大小。
  • 延展:要用於調整 widget 大小量值。功能是自動調整規模函式相同。為實際上,將會執行相同的程式碼。
  • 父代:Widget 的父系。如果未指定,widget 會加入至 Main 配置在 Android 上或根檢視控制器 (特別是對 Window.RootViewController.View) iOS 上。

位置引數的可能值為 Android RelativeLayout.LayoutParams 類別非常類似項目。它們可以是任何一個:"置中,""LEFT"、"RIGHT,""TOP,"「 下 」,「 ALIGN_LEFT,""ALIGN_RIGHT,""ALIGN_TOP,""ALIGN_BOTTOM,""ALIGN_PARENT_TOP,""ALIGN_PARENT_BOTTOM。 」

這些參數可用來在 iOS 和 Android 上的水平和垂直放置。不需要的任何 XML 或 XAML 的知識。而且沒有任何 iOS 分鏡腳本來處理。

位置建立之後,您會在其中放置 widget。以下是這樣的一般語法:

AddWidget(location, widgetName, initParameter, width, height);

AddButton 是特定的情況下,這類函式,其中初始化引數是在按鈕上顯示的文字。Widget 函式的其他範例會 AddCombobox AddLabel,AddView,而且有更多,您會看到。

AddAction 函式會將動作指派給按鈕中,當使用者按一下它時。它通常會有下列語法:

AddAction(widgetName, callbackFunction);

回呼函式中 CSCS 一律具有兩個參數,寄件者內容引數,取自 C# 的概念。

Talk_click 函式內第一次我呼叫 ShowToast 函式,在 Android 和 iOS 上的自訂類似快顯通知的實作會呼叫原生快顯通知的實作。IOS 實作只要建構小型的畫面格的訊息,並在逾時後將其終結。

最後,我呼叫語音辨識函式:

VoiceRecognition("voice_recog", voice = "en-US");

第一個參數是完整的語音辨識時要呼叫的回呼函式的名稱。第二個參數是語音。它是選擇性,並根據預設,它是美國英文。語音 (例如指定為語言名稱的 ISO 639-1 代碼與 ISO 3166-1 國家/地區的程式碼的擴充 alpha 2。"EN-US"代表英文,美國、"ES-MX"西班牙文,墨西哥"PT-BR",如葡萄牙文、 巴西等等)。

語音辨識的回呼函式簽章如下所示:

function voice_recog(errorStatus, recognized)

ErrorStatus 引數會在成功和失敗的錯誤描述為空字串。如果函式成功,辨識的字會傳遞做為第二個參數。如果沒有出現,警示] 對話方塊會顯示給使用者 (實作為 UIAlertController ios 以及 Android 上 AlertDialog.Builder)。如果成功語音辨識,文字轉換語音 Speak 會呼叫函數。它有下列簽章:

說話 (wordToPronounce,語音 ="EN-US");

執行指令碼的結果圖 1如下所示圖 2。在左邊,表示 iPhone 中,此圖顯示成功的語音辨識,唸成的 word 已辨識時。在右側,代表 Android,圖中顯示失敗,沒有麥克風 (常見的情況時,使用模擬器) 系統上安裝時。

範例會執行"Hello,World !" 在 iPhone (左) 和 Android (右) 的指令碼
圖 2 範例會執行"Hello,World !" 在 iPhone (左) 和 Android (右) 的指令碼

專案的一般結構

工作流程中將 CSCS 執行程式碼是?答案是適用於 iOS 和 Android 專案不同。您會看到它在何種如下所示,但是的完整詳細資料位於在隨附原始程式碼下載github.com/vassilych/mobile

常見的程式碼,並使用兩個平台,會在共用的專案部分中,指令碼。共用,其中包含所有 C# 檔案剖析 CSCS 程式碼所需。每個平台專屬的程式碼是位於 scripting.iOS 和指令碼。Droid 專案。請參閱範例專案中的結構圖 3

Xamarin 專案 CSCS 指令碼的一般結構
圖 3 的指令碼之後 CSCS Xamarin 專案的一般結構

實際的 CSCS 指令碼位於 msdnScript.cscs 下的檔案中的指令碼的 [資源] 資料夾中。共用的專案。請注意,您可以藉由呼叫下列 CSCS 函式包含其他 CSCS 檔案:

ImportFile("anotherScriptFile.cscs");

針對 Android 專案設定連結至 msdnScript.cscs 檔案從指令碼。Droid Assets 資料夾,並設定連結 scripting.iOS 資源資料夾從 iOS 專案。您也可以參考的指令碼中的許多種,例如保留在不同的平台上的不同版本的指令碼。

CommonFunctions.cs 檔案包含通用 iOS 和 Android 的功能。特別是,它會保存執行 msdnScripting.cscs 指令碼所示的方法圖 4。請注意,我區分使用前置處理器 __IOS__ 和 __ANDROID__ 指示詞的 iOS 和 Android 專屬程式碼。平台專屬的程式碼通常位於中對應的專案,scripting.iOS 或指令碼。Droid。

圖 4 執行 CSCS 指令碼

public static void RunScript()
{
  RegisterFunctions();
  string fileName = "msdnScript.cscs";
  string script = "";
#if __ANDROID__
  Android.Content.Res.AssetManager assets = MainActivity.TheView.Assets;
  using (StreamReader sr = new StreamReader(assets.Open(fileName))) {
    script = sr.ReadToEnd();
  }
#endif
#if __IOS__
  string[] lines = System.IO.File.ReadAllLines(fileName);
  script = string.Join("\n", lines);
#endif
  Variable result = null;
  try {
    result = Interpreter.Instance.Process(script);
  } catch (Exception exc) {
    Console.WriteLine("Exception: " + exc.Message);
    Console.WriteLine(exc.StackTrace);
    ParserFunction.InvalidateStacksAfterLevel(0);
    throw;
  }
}

您要在呼叫的 RunScript 函式?您可以呼叫它已初始化的全域配置之後,才讓您可以將 widget 加入它。

結果是棘手這麼做在 Android 與 iOS 上:只呼叫 RunScript 函式結尾的 MainActivity.OnCreate 函式會失敗,因為部分變數尚未尚未初始化。因此您必須在主要活動實際上會開始執行前將 RunScript 權限。Android 活動 Lifestyle 文件,網址goo.gl/yF8dTZ提供相關線索:MainActivity.OnResume 方法完成之後,它必須移右。部分全域變數 (例如,螢幕大小、 方向等) 都尚未初始化 OnResume 方法結尾處,甚至讓訣竅是註冊將會盡全域觸發 OnResume 方法結尾處的全域配置監看員 版面配置的建構:

protected override void OnResume()
{
  base.OnResume();
  if (!m_scriptRun) {
    ViewTreeObserver vto = TheLayout.ViewTreeObserver;
    vto.AddOnGlobalLayoutListener(new LayoutListener());
    m_scriptRun = true;
  }
}

請注意,我使用特殊的布林值變數 m_scriptRun 確定指令碼會執行一次。OnGlobalLayout 中的方法配置的接聽程式,然後執行指令碼:

public class LayoutListener : 
  Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
  public void OnGlobalLayout()
  {
    var vto = MainActivity.TheLayout.ViewTreeObserver;
    vto.RemoveOnGlobalLayoutListener(this);
    CommonFunctions.RunScript();
  }
}

適用於 iOS 是稍微更容易,您可以只在 AppDelegate.FinishedLaunching 方法結尾處執行指令碼的情況。

Text-to-Speech

我們來看看如何將一些功能新增至 CSCS,使用文字轉換語音,做為範例。

首先,我需要建立 ParserFunction 類別衍生類別並覆寫其受保護的虛擬評估方法,如中所示圖 5

圖 5 說話函式實作

public class SpeakFunction : ParserFunction
{
  protected override Variable Evaluate(ParsingScript script)
  {
    bool isList = false;
    List<Variable> args = Utils.GetArgs(script,
         Constants.START_ARG, Constants.END_ARG, out isList);
    Utils.CheckArgs(args.Count, 1, m_name);
    TTS.Init();
    string phrase = args[0].AsString();
    TTS.Voice     = Utils.GetSafeString(args, 1, TTS.Voice);
    TTS.Speak(phrase);
    return Variable.EmptyInstance;
  }
}

這個類別是只包裝函式上的實際文字轉換語音的實作。對於 iOS,文字轉換語音的實作所示圖 6 Android 實作類似,但花費更多程式碼撰寫。您可以看到它隨附原始程式碼下載中。

圖 6 iOS 文字轉換語音實作 (片段)

using AVFoundation;
namespace scripting.iOS
{
  public class TTS
  {
    static AVSpeechSynthesizer g_synthesizer = new AVSpeechSynthesizer();
    static public float  SpeechRate { set; get; }      = 0.5f;
    static public float  Volume { set; get; }          = 0.7f;
    static public float  PitchMultiplier { set; get; } = 1.0f;
    static public string Voice { set; get; }           = "en-US";
    static bool m_initDone;
    public static void Init()
    {
      if (m_initDone) {
        return;
      }
      m_initDone = true;
      // Set the audio session category, then it will speak
      // even if the mute switch is on.
      AVAudioSession.SharedInstance().Init();
      AVAudioSession.SharedInstance().SetCategory(AVAudioSessionCategory.Playback,
         AVAudioSessionCategoryOptions.DefaultToSpeaker);
    }
    public static void Speak(string text)
    {
      if (g_synthesizer.Speaking) {
        g_synthesizer.StopSpeaking(AVSpeechBoundary.Immediate);
      }
      var speechUtterance = new AVSpeechUtterance(text) {
        Rate = SpeechRate * AVSpeechUtterance.MaximumSpeechRate,
        Voice = AVSpeechSynthesisVoice.FromLanguage(Voice),
        Volume = Volume,
        PitchMultiplier = PitchMultiplier
      };
      g_synthesizer.SpeakUtterance(speechUtterance);
    }
  }
}

實作之後,我需要將它插入剖析器。這是共用專案 CommonFunctions.RegisterFunctions 靜態方法中 (也顯示圖 3):

ParserFunction.RegisterFunction("Speak", new SpeakFunction());

語音辨識

語音辨識我需要使用回呼函式以哪些 word,告訴使用者實際上已辨識 (或報告錯誤,像是圖 2)。

我們即將實作語音辨識的兩個函式,另一個是啟動 [語音辨識並使用另一個取消它。這兩個函數會將數字向剖析器,就如同我註冊前一節中的文字轉換語音:

ParserFunction.RegisterFunction("VoiceRecognition", new VoiceFunction());
ParserFunction.RegisterFunction("StopVoiceRecognition", new StopVoiceFunction());

適用於 iOS 的這兩個函數的實作所示圖 7。適用於 Android 實作類似,但請注意,語音辨識已加入 iOS 只有在版本 10.0,因此我們必須檢查裝置版本,如有必要,通知使用者的裝置不支援它之前 10.0 的 iOS 版本中。

圖 7 語音辨識實作

public class VoiceFunction : ParserFunction
{
  static STT m_speech = null;
  public static  STT LastRecording { get { return m_speech; }}
  protected override Variable Evaluate(ParsingScript script)
  {
    bool isList = false;
    List<Variable> args = Utils.GetArgs(script,
                          Constants.START_ARG, Constants.END_ARG, out isList);
    Utils.CheckArgs(args.Count, 1, m_name);
    string strAction = args[0].AsString();
    STT.Voice = Utils.GetSafeString(args, 1, STT.Voice).Replace('_', '-');
    bool speechEnabled = UIDevice.CurrentDevice.CheckSystemVersion(10, 0);
    if (!speechEnabled) {
      UIVariable.GetAction(strAction, "\"" +
       string.Format("Speech recognition requires iOS 10.0 or higher.
       You have iOS {0}",
                     UIDevice.CurrentDevice.SystemVersion) + "\"", "");
      return Variable.EmptyInstance;
    }
    if (!STT.Init()) {
      // The user didn't authorize accessing the microphone.
      return Variable.EmptyInstance;
    }
    UIViewController controller = AppDelegate.GetCurrentController();
    m_speech = new STT(controller);
    m_speech.SpeechError += (errorStr) => {
      Console.WriteLine(errorStr);
      controller.InvokeOnMainThread(() => {
        UIVariable.GetAction(strAction, "\"" + errorStr + "\"", "");
      });
    };
    m_speech.SpeechOK += (recognized) => {
      Console.WriteLine("Recognized: " + recognized);
      controller.InvokeOnMainThread(() => {
        UIVariable.GetAction(strAction, "", "\"" + recognized + "\"");
      });
    };
    m_speech.StartRecording(STT.Voice);
    return Variable.EmptyInstance;
  }
}
public class StopVoiceFunction : ParserFunction
{
  protected override Variable Evaluate(ParsingScript script)
  {
    VoiceFunction.LastRecording?.StopRecording();
    script.MoveForwardIf(Constants.END_ARG);
    return Variable.EmptyInstance;
  }
}

實際的語音辨識程式碼為 SST 類別。它顯示在這裡的位元太長,而且是也適用於 iOS 和 Android 的不同。邀請您隨附的原始程式碼中將它簽出。

我沒有回呼函式在文字轉換語音,但您可以加入一個類似的方式,讓使用者語音完成時 (或發生錯誤)。回呼 CSCS 程式碼會執行叫用 UIVariable.GetAction 方法:

public static Variable GetAction(string funcName, string senderName, string eventArg)
{
  if (senderName == "") {
    senderName = "\"\"";
  }
  if (eventArg == "") {
    eventArg = "\"\"";
  }
  string body = string.Format("{0}({1},{2});", funcName, senderName, eventArg);
  ParsingScript tempScript = new ParsingScript(body);
  Variable result = tempScript.ExecuteTo();
  return result;
}

您可以看到此函數是否用於圖 7

範例:貨幣轉換程式

為開發跨平台應用程式使用不同的 CSCS 功能的範例,讓我們來從頭開始建立應用程式 — 貨幣轉換程式。

若要取得最新匯率應用程式中,您必須使用的線上服務。選擇 [ exchangerate api.com。站台提供的免費的每個月前 1,000 個要求的簡單用 Web 服務應該足以開始。註冊之後,您會取得您必須提供每個要求的唯一索引鍵。

我的應用程式會在縱向或橫向模式中有不同的檢視。圖 8顯示直向模式和圖 9顯示橫向 iPhone 和 Android 的模式。

在 iPhone (左) 和 Android (右) 上的直向模式中的貨幣轉換程式
圖 8 iPhone (左) 和 Android (右) 上的直向模式中的貨幣轉換程式

以橫向模式 Android (下方) 和 iPhone (上方) 上的貨幣轉換程式
圖 9 iPhone (上方) 和 Android (下方) 上的橫向模式中的貨幣轉換程式

圖 10包含整個 CSCS 實作的貨幣轉換程式應用程式。

圖 10 CSCS 實作的貨幣轉換程式應用程式

function on_about(sender, arg) {
  OpenUrl("http://www.exchangerate-api.com");
}
function on_refresh(sender, arg) {
  currency1 = GetText(cbCurrency1);
  currency2 = GetText(cbCurrency2);
  currency_request(currency1, currency2);
}
function currency_request(currency1, currency2) {
  if (currency1 == currency2) {
    time = Now("HH:mm:ss");
    date = Now("yyyy/MM/dd");
    rate = 1;
  } else {
    url = apiUrl + currency1 + "/" + currency2;
    try {
      data = WebRequest(url);
    } catch(exception) {
      WriteConsole(exception.Stack);
      ShowToast("Couldn't get rates. " + exception);
      SetText(labelRateValue, "Error");
      return;
    }
    try {
      timestamp = StrBetween(data, "\"timestamp\":", ",");
      time      = Timestamp(timestamp, "HH:mm:ss");
      date      = Timestamp(timestamp, "yyyy/MM/dd");
      rate      = StrBetween(data, "\"rate\":", "}");
    } catch(exception) {
      ShowToast("Couldn't get rates. " + exception);
      SetText(labelRateValue, "Error");
      return;
    }
  }
  SetText(labelRateValue, rate);
  SetText(labelDateValue, date);
  SetText(labelTimeValue, time);
}
function init() {
  currencies = {"EUR", "USD", "GBP", "CHF", "JPY", "CNY", "MXN", "RUB", "BRL", "SAR"};
  flags      = {"eu_EU", "en_US", "en_GB", "de_CH", "ja_JP", "zh_CN",
                "es_MX", "ru_RU", "pt_BR", "ar_SA"};
  AddWidgetData(cbCurrency1, currencies);
  AddWidgetImages(cbCurrency1, flags);
  SetSize(cbCurrency1, 80, 40);
  SetText(cbCurrency1, "USD");
  AddWidgetData(cbCurrency2, currencies);
  AddWidgetImages(cbCurrency2, flags);
  SetSize(cbCurrency2, 80, 40);
  SetText(cbCurrency2, "MXN");
  SetImage(buttonRefresh,     "coins");
  AddAction(buttonRefresh,    "on_refresh");
  SetFontColor(buttonRefresh, "white");
  SetFontSize(buttonRefresh,  20);
  AddAction(aboutButton,      "on_about");
}
function on_portrait(sender, arg) {
  AddOrSelectTab("Rates", "rates_active.png", "rates_inactive.png");
  SetBackground("us_bg.png");
  locCurrency1 = GetLocation("ROOT", "LEFT", "ROOT", "TOP", 10, 80);
  AddCombobox(locCurrency1, "cbCurrency1", "", 280, 100);
  locCurrency2 = GetLocation("ROOT", "RIGHT", cbCurrency1, "CENTER", -10);
  AddCombobox(locCurrency2, "cbCurrency2", "", 280, 100);
  locRateLabel = GetLocation("ROOT", "CENTER", cbCurrency2, "BOTTOM", -80, 60);
  AddLabel(locRateLabel, "labelRate", "Rate:", 200, 80);
  locRateValue = GetLocation("ROOT", "CENTER", labelRate, "CENTER", 100);
  AddLabel(locRateValue, "labelRateValue", "", 240, 80);
  locDateLabel = GetLocation("ROOT", "CENTER", labelRate, "BOTTOM", -80);
  AddLabel(locDateLabel, "labelDate", "Date:", 200, 80);
  locDateValue = GetLocation("ROOT", "CENTER", labelDate, "CENTER", 100);
  AddLabel(locDateValue, "labelDateValue", "", 240, 80);
  locTimeLabel = GetLocation("ROOT", "CENTER", labelDate, "BOTTOM", -80);
  AddLabel(locTimeLabel, "labelTime", "Time:", 200, 80);
  locTimeValue = GetLocation("ROOT", "CENTER", labelTime, "CENTER", 100);
  AddLabel(locTimeValue, "labelTimeValue", "", 240, 80);
  locRefresh = GetLocation("ROOT", "CENTER", "ROOT", "BOTTOM", 0, -4);
  AddButton(locRefresh, "buttonRefresh", "Convert", 200, 100);
  AddOrSelectTab("Settings", "settings_active.png", "settings_inactive.png");
  locAbout = GetLocation("ROOT", "CENTER", "ROOT", "BOTTOM", -4);
  AddButton(locAbout, "aboutButton", "Powered by exchangerate-api.com", 360, 100);
}
function on_landscape(sender, arg) {
  AddOrSelectTab("Rates", "rates_active.png", "rates_inactive.png");
  SetBackground("us_w_bg.png");
  locCurrency1 = GetLocation("ROOT", "LEFT", "ROOT", "CENTER", 50);
  AddCombobox(locCurrency1, "cbCurrency1", "", 200, 120);
  locCurrency2 = GetLocation(cbCurrency1, "RIGHT", "ROOT", "CENTER", 40);
  AddCombobox(locCurrency2, "cbCurrency2", "", 200, 120);
  locDateLabel = GetLocation(cbCurrency2, "RIGHT", "ROOT", "CENTER", 60);
  AddLabel(locDateLabel, "labelDate", "Date:", 180, 80);
  locDateValue = GetLocation(labelDate, "RIGHT", labelDate, "CENTER", 10);
  AddLabel(locDateValue, "labelDateValue", "", 220, 80);
  locRateLabel = GetLocation(cbCurrency2, "RIGHT", labelDate, "TOP", 60);
  AddLabel(locRateLabel, "labelRate", "Rate:", 180, 80);
  locRateValue = GetLocation(labelRate, "RIGHT", labelRate, "CENTER", 10);
  AddLabel(locRateValue, "labelRateValue", "", 220, 80);
  locTimeLabel = GetLocation(cbCurrency2, "RIGHT", labelDate, "BOTTOM", 60);
  AddLabel(locTimeLabel, "labelTime", "Time:", 180, 80);
  locTimeValue = GetLocation(labelTime, "RIGHT", labelTime, "CENTER", 10);
  AddLabel(locTimeValue, "labelTimeValue", "", 220, 80);
  locRefresh = GetLocation("ROOT", "CENTER", "ROOT", "BOTTOM", 0, -4);
  AddButton(locRefresh, "buttonRefresh", "Convert", 180, 90);
  AddOrSelectTab("Settings", "settings_active.png", "settings_inactive.png");
  locAbout = GetLocation("ROOT", "CENTER", "ROOT", "BOTTOM", -4);
  AddButton(locAbout, "aboutButton", "Powered by exchangerate-api.com", 360, 100);
}
AutoScale();
apiUrl = "https://v3.exchangerate-api.com/pair/c2cd68c6d7b852231b6d69ee/";
RegisterOrientationChange("on_portrait", "on_landscape");
init();
if (Orientation == "Portrait") {
  on_portrait("", "");
} else {
  on_landscape("", "");
}
SelectTab(0);

函式 on_about 和 on_refresh 是這兩個回呼時,發生在使用者按一下按鈕上。

當使用者按一下"Powered by"中的按鈕會導致 OpenUrl 函式,以開啟 [設定] 索引標籤上,執行 on_about 方法exchangerate api.com在預設瀏覽器的首頁 (此索引標籤不會顯示在圖 8圖 9)。當使用者按一下 [轉換] 按鈕時,會執行 on_refresh 方法。接著您可取得所選的貨幣和 CSCS currency_request 函式會叫用,它會執行實際的速度轉換。

Currency_request 函式會先檢查這兩種貨幣是否相同,在此情況下我已經知道比率為 1,並不需要呼叫 Web 服務 (我想要儲存這項服務每個月我免費有限的用途)。否則,會呼叫 WebRequest 函式。此函式是這兩個 ios 和 Android 的一般和它的實作所示圖 11。請注意,您不必執行在 C# 程式碼中處理的例外狀況。如果發生例外狀況 (例如,如果服務無法使用),例外狀況會傳播 CSCS 的程式碼,會攔截。請注意 WebRequest 函式以同步方式實作。您可以也會使非同步藉由提供回呼函式呼叫完成要求 (相當於先前已顯示的語音辨識功能)。

圖 11 C# 實作 WebRequestFunction 評估方法

public class WebRequestFunction : ParserFunction
{
  protected override Variable Evaluate(ParsingScript script)
  {
    bool isList = false;
    List<Variable> args = Utils.GetArgs(script,
                          Constants.START_ARG, Constants.END_ARG, out isList);
    Utils.CheckArgs(args.Count, 1, m_name);
    string uri = args[0].AsString();
    string responseFromServer = "";
    WebRequest request = WebRequest.Create(uri);
    using (WebResponse response = request.GetResponse()) {
      Console.WriteLine("{0} status: {1}", uri,
                        ((HttpWebResponse)response).StatusDescription);
      using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
        responseFromServer = sr.ReadToEnd();
      }
    }
    return new Variable(responseFromServer);
  }
}

讓我們繼續 CSCS 程式碼中的分析圖 10。我已描述 currency_request 函式中發生的事。中的 JSON 回應exchangerate api.com看起來像下面這樣:

{"result":"success","timestamp":1511464063,"from":"USD","to":"CHF",­"rate":­0.99045395}

時間戳記是自 1970 年 1 月 1 日以來過的秒數。Timestamp(format) CSCS 函式會將此秒數轉換成指定的日期或時間格式。

StrBetween 資料、 strStart (strEnd) 是方便的功能,從 strStart1 和 strStart2 字串之間的資料字串擷取子字串。

一旦擷取速率,日期和時間,我使用 SetText (widgetName、 文字) 函式對應的標籤來設定它們。

我可以在 init 函式初始化資料,而且可以加入其他轉換貨幣。

很容易讓有不同的版面配置,針對不同的方向: 方向變更回呼向 RegisterOrientationChange 函式。每次裝置方向變更時,會呼叫的 on_portrait 和 on_landscape 函式。您可以看到在底部圖 10,藉由叫用設定:

RegisterOrientationChange("on_portrait", "on_landscape");

一般情況下您可以將 widget 加入在特定位置螢幕,使用邏輯說明"Hello,World !"的範例中的第一個區段。您可能注意到橫向和縱向模式的另一個電話背景。這是使用 SetBackground(imageName) CSCS 函式。

AddOrSelectTab 函式具有下列簽章:

AddOrSelectTab(Tab_Name, Picture_when_active, Picture_when_inactive);

如果] 索引標籤還不存在,它就會加入,否則選取和所有連續的 widget 就會加入至這個索引標籤。圖 12顯示索引標籤作用中和非使用中模式中的外觀。

使用中和在 iOS 上的非使用中索引標籤
圖 12 主動和 iOS 上的非使用中索引標籤

總結

在本文中您會看到與 CSCS 您可以藉由程式設計使用指令碼語言的行動裝置應用程式。指令碼會轉換成原生程式碼使用 C# 解譯器和 Xamarin 架構。CSCS 指令碼可以執行在 C# (和 Xamarin C# 中可以執行任何動作,可以在原生應用程式開發) 可執行的任何動作。

我已發佈 CSCS 完全以撰寫的應用程式。簽出的 iOS 版本apple.co/2yixGxZ和 Android 的版本在goo.gl/zADtNb

CSCS 針對行動裝置應用程式的指令碼是不完整。要加入 CSCS 新功能,請建立衍生自 ParserFunction 類別的新類別並覆寫其評估方法。接著,您註冊該類別與剖析器,提供其 CSCS 名稱:

ParserFunction.RegisterFunction("CSCS_Name", new MyNewCustomFunction())

使用 CSCS,您可以將所有小工具,以程式設計的方式,和相同的程式碼會使用適用於 Android 和 iOS。您不需要任何 XAML 使用,如同使用 Xamarin.Forms。

您也可以與現有 C# 程式碼結合 CSCS — 我所述在很容易就能從 CSCS,呼叫 C# 程式碼codemag.com/article/1711081。在該文件中,您也可以檢查 CSCS 中實作的函式的清單。但最新、 最新 CSCS 函式和功能,請瀏覽github.com/vassilych/mobile

不幸的是,沒有空間討論您可以在 CSCS,應用程式內購買和計費,在應用程式的公告,例如排程單次且重複性高的事件,以及更多,某些其他酷事情,但您可以簽出中隨附的原始程式碼下載。

Vassili Kaplan是先前的 Microsoft Lync 開發人員。他的熱情在 C#、 c + +、 Python 和現在 CSCS 的程式設計。他目前位於蘇黎世,瑞士,並為 freelancer 的各種不同的運作方式。您可以連線到他處iLanguage.ch

非常感謝下列 Microsoft 技術專家檢閱這篇文章:James McCaffrey
Dr。James McCaffrey 適用於 Microsoft Research Redmond,Wash.他已投入許多 Microsoft 產品,包括 Internet Explorer 和 Bing。Dr。在可到達 McCaffrey jamccaff@microsoft.com


MSDN Magazine 論壇中的這篇文章的討論