Febbraio 2018

Volume 33 Numero 2

Il presente articolo è stato tradotto automaticamente.

C# - Scrittura di app per dispositivi mobili native con un linguaggio di scripting personalizzabile

Da Vassili Kaplan

Problema di febbraio 2016 di MSDN Magazine, ho illustrato come creare un linguaggio di scripting personalizzato in base all'algoritmo di divisione e unione per l'analisi delle espressioni matematiche in c# (msdn.com/magazine/mt632273). Chiamato il linguaggio di Scripting personalizzabile in c# o CSCS. Di recente è stato pubblicato un E-book forniti ulteriori dettagli sulla creazione di una lingua personalizzata (bit.ly/2yijCod). Creazione di un proprio linguaggio di scripting non sembrare inizialmente essere particolarmente utile, anche se sono presenti alcune applicazioni interessanti di esso (ad esempio, inganno gioco). Alcune applicazioni si è verificato anche nella programmazione di Unity.

Ma rilevato quindi un'applicazione per un linguaggio di scripting personalizzabile ancora più interessante: scrittura di applicazioni multipiattaforma per dispositivi mobili. Si scopre che è possibile utilizzare CSCS scrittura di App per Android e iOS (e Windows Phone possono essere facilmente aggiunte, nonché). E lo stesso codice può essere utilizzato per tutte le piattaforme. Dopo la pubblicazione di un'introduzione su come eseguire questa operazione in numero di novembre-dicembre 2017 di Magazine di codice (codemag.com/article/1711081).

In questo articolo verrà per richiedere un approfondimento e viene illustrato come utilizzare CSCS programmare per i dispositivi mobili. Correggerà anche alcune imprecisioni nell'articolo di rivista di codice. Si noterà che tutto ciò che può essere eseguita su una piattaforma nativa può essere effettuata in CSCS. Farò anche adesso per mostrare come è possibile aggiungere le funzionalità mancanti CSCS in tempo reale.

Per eseguire il codice illustrato in questo articolo, è necessario Visual Studio 2017 con Xamarin, installato in Windows o su macOS. Personalmente uso Visual Studio Community Edition 2017 nel mio MacBook. Si noti che un computer Mac è necessario distribuire le app iOS in App Store di Apple.

Un "Hello, World!" per le applicazioni mobili

Dare un'occhiata figura 1, che illustra alcune codice CSCS di base per il riconoscimento vocale e di sintesi vocale. Esaminare il codice riga per riga.

Figura 1, "Hello, World!" in CSCS App per dispositivi mobili

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);
  }
}

La funzione di scalabilità automatica consente di regolare automaticamente le dimensioni di widget in base alle dimensioni dello schermo dispositivo reale. Ad esempio, con scalabilità automatica, la larghezza di un widget sarà due volte più grande in un dispositivo con una larghezza pari a 1280 pixel come in uno con una larghezza di 640 pixel. La firma della funzione di scalabilità automatica effettiva è:

AutoScale(scale = 1.0);

Se non si utilizza la dimensione predefinita = 1.0 parametro, il parametro della scala specificata verrà applicato alla differenza. Ad esempio, se la scala = 0,5 la differenza nel widget di dimensioni quando lo spostamento da 640 a 1280 pixel non sarà due volte ma 1,5 volte poiché la formula per calcolare la nuova dimensione:

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

Tuttavia, se ridimensionare = 2, il widget sarà 3 volte la dimensione in base al calcolo. Un caso speciale della scala = 0 soddisfa anche qui la formula: Non verrà eseguita alcuna regolazione di scala, il widget avrà esattamente la stessa dimensione indipendentemente dalla dimensione della periferica. Questo parametro di scala può essere applicato anche al widget, può essere specificato come parametro facoltativo nella funzione GetLocation. Illustrato come eseguire questa operazione in un bit.

Successivamente, definire una variabile vocale. Si noti che CSCS è un linguaggio di script Python simile, ovvero il tipo della variabile viene dedotto dal contesto, pertanto la variabile vocale sarà rappresentata come una stringa in c# dietro le quinte.

Definire quindi un pulsante. Una definizione di widget in CSCS sempre accetta due istruzioni: La prima specifica la posizione del widget e la seconda è la definizione effettiva del widget. Dietro le quinte, un widget UIButton viene utilizzato per iOS e un pulsante viene utilizzato per Android.

La sintassi generale per la creazione di una posizione sullo schermo è:

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

Di seguito è riportato il significato degli argomenti:

  • ReferenceX: Il nome del widget un'altra per la posizione orizzontale. Può essere la stringa "ROOT", vale a dire il widget padre o la schermata principale.
  • PlacementX: Un punto orizzontale rispetto alla widget indicati in ReferenceX. Alla fine di questi argomenti vengono elencati i valori possibili.
  • ReferenceY: Il nome di un altro widget per il posizionamento verticale. Può essere la stringa "ROOT", vale a dire il widget padre o la schermata principale.
  • PlacementY: Un punto rispetto al widget verticale indicati in ReferenceY. I valori possibili sono elencati alla fine Questi argomenti.
  • AdjustmenX: Spostamento orizzontale aggiuntive del widget in pixel. Può anche essere negativo; la direzione positivo passa da sinistra a destra.
  • AdjustmenY: Spostamento verticale aggiuntivo del widget in pixel. Può anche essere negativo; la direzione positivo passa dall'alto verso il basso.
  • ScaleOption: Indica se applicare una particolare opzione di ridimensionamento per il widget. Se questa opzione è false o non viene specificato, verrà eseguita la regolazione specificata nella funzione di scalabilità automatica. Se questa opzione è disponibile, i parametri di regolazione e le dimensioni del widget verrà modificati in base al parametro di scala.
  • Scalabilità: La misura da utilizzare per l'adattamento delle dimensioni del widget. La funzionalità è uguale a quello la funzione di scalabilità automatica. In realtà, verrà eseguito lo stesso codice.
  • Padre: L'elemento padre del widget. Se non specificato, verrà aggiunto il widget per il Layout principale in Android o per il Controller di visualizzazione principale in iOS (in particolare per Window.RootViewController.View).

I valori possibili per gli argomenti di posizione sono molto simili alla classe RelativeLayout.LayoutParams Android. Può trattarsi di uno di: "CENTRO" "LEFT", "RIGHT", "TOP," "BOTTOM", "ALIGN_LEFT," "ALIGN_RIGHT", "ALIGN_TOP", "ALIGN_BOTTOM", "ALIGN_PARENT_TOP", "ALIGN_PARENT_BOTTOM".

Questi parametri vengono utilizzati per la selezione host orizzontali e verticali in iOS e Android. Non è necessaria alcuna conoscenza XAML o XML. Ed non è disponibile alcun iOS Storyboard per affrontare.

Una volta creato il percorso, inserire un widget in essa contenuti. Di seguito è riportata la sintassi generale per questa operazione:

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

AddButton è un caso particolare di una funzione, in cui l'argomento di inizializzazione è il testo visualizzato sul pulsante. Altri esempi di funzioni di widget sono AddCombobox AddLabel, AddView e vi sono molti altri, come si vedrà.

La funzione AddAction assegna un'azione a un pulsante quando l'utente fa clic su di esso. È in genere la sintassi seguente:

AddAction(widgetName, callbackFunction);

Una funzione di callback in CSCS ha sempre due parametri, un mittente e un argomento di contesto, un concetto presa in prestito da c#.

All'interno della funzione, talk_click prima chiamare la funzione ShowToast, che chiama un'implementazione di tipo avviso popup nativa in Android e un'implementazione personalizzata di tipo avviso popup in iOS. L'implementazione di iOS sufficiente costruisce una cornice di piccole dimensioni con un messaggio e viene eliminata dopo un timeout.

Infine, per la funzione di riconoscimento vocale chiamare:

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

Il primo parametro è il nome della funzione di callback da chiamare quando il riconoscimento vocale è stato completato. Il secondo parametro è la voce. È facoltativo e, per impostazione predefinita, è Stati Uniti Inglese. Le voci vengono specificate come codice ISO 639-1 per il nome della lingua e ISO 3166-1 alfa-2 per il codice paese (ad esempio. "en-US" per l'inglese, Stati Uniti, "es-MX" per lo spagnolo, Messico, "pt-BR", per portoghese (Brasile) e così via).

La firma della funzione di callback del riconoscimento vocale è la seguente:

function voice_recog(errorStatus, recognized)

L'argomento valori errorStatus sarà una stringa vuota in caso di esito positivo e una descrizione dell'errore in caso di errore. Se la funzione ha esito positivo, la parola riconosciuta viene passata come secondo parametro. In caso contrario, verrà visualizzato una finestra di dialogo di avviso all'utente (implementato come un UIAlertController in iOS e come un AlertDialog.Builder in Android). Se il riconoscimento vocale ha esito positivo, la funzione di sintesi vocale Speak verrà essere chiamato. Ha la firma seguente:

Leggi (wordToPronounce, vocale = "en-US");

I risultati dell'esecuzione dello script in figura 1 vengono visualizzati figura 2. La figura a sinistra, che rappresenta un iPhone, Mostra il riconoscimento vocale ha esito positivo, quando una parola pronuncia è stata riconosciuta. La figura a destra, che rappresenta un Android, Mostra un errore, quando è presente alcun microfono installato nel sistema (caso comune quando si utilizza un simulatore).

Un esempio di esecuzione di "Hello, World!" Script in un iPhone (a sinistra) e in Android (a destra)
Figura 2, un esempio di esecuzione di "Hello, World!" Script in un iPhone (a sinistra) e in Android (a destra)

Struttura generale del progetto

In cui nel flusso di lavoro verrà CSCS essere eseguito il codice? La risposta è diversa per i progetti iOS e Android. Verrà visualizzato di seguito, ma sono i dettagli completi nel download del codice sorgente associato al github.com/vassilych/mobile.

Il codice comune, utilizzato da entrambe le piattaforme, è in parte il progetto condiviso di scripting. Condiviso, che contiene tutti i file c# necessari per l'analisi del codice CSCS. Il codice specifico per ogni piattaforma è situato nella scripting.iOS e di scripting. Progetti Droid. Vedere la struttura di un progetto di esempio in figura 3.

Struttura generale di un progetto Xamarin CSCS di scripting
Figura 3 struttura generale di un progetto Xamarin CSCS di scripting

Lo script CSCS si trova nel file msdnScript.cscs nella cartella risorse in cui la creazione di script. Progetto condiviso. Si noti che è possibile includere altri file CSCS chiamando la funzione CSCS seguente:

ImportFile("anotherScriptFile.cscs");

Per il progetto Android imposta un collegamento al file msdnScript.cscs dalla creazione di script. Cartella Droid asset e per il progetto iOS impostare un collegamento dalla cartella scripting.iOS risorse. È possibile anche fare riferimento allo script in diversi modi, ad esempio mantenere versioni diverse dello script su piattaforme diverse.

Il file CommonFunctions.cs contiene funzionalità comuni per iOS e Android. In particolare, contiene il metodo che esegue lo script msdnScripting.cscs che viene visualizzato in figura 4. Si noti che distinguono tra il codice iOS e Android specifici mediante le direttive del preprocessore __IOS__ e __ANDROID__. Il codice specifico della piattaforma si trova principalmente nei progetti corrispondente, scripting.iOS o script. Droid.

Figura 4, l'esecuzione dello Script 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;
  }
}

In cui viene chiamato la funzione RunScript? È possibile chiamarlo solo dopo che è stato inizializzato il layout globale, è possibile aggiungere widget a esso.

Si evince che risulta più difficile eseguire questa operazione in Android rispetto a in iOS: Solo la chiamata alla funzione di RunScript alla fine della funzione MainActivity.OnCreate ha esito negativo perché alcune variabili non è stato ancora inizializzati. Pertanto, è necessario inserire RunScript destra prima che venga effettivamente avviata l'attività principale in esecuzione. Documentazione di Android attività Lifestyle goo.gl/yF8dTZ fornisce un'indicazione: È necessario andare direttamente al termine il metodo MainActivity.OnResume. Alcune variabili globali (ad esempio, le dimensioni dello schermo, l'orientamento e così via) non sono ancora inizializzati anche alla fine del metodo OnResume, pertanto lo stratagemma consiste nel registrare un controllo di layout globale alla fine del metodo OnResume che verrà attivato appena globale layout viene costruito:

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

Si noti che una speciale m_scriptRun variabile booleana per assicurarsi che lo script viene eseguito una sola volta. Il metodo di OnGlobalLayout nel listener di layout viene quindi eseguito lo script:

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

Per iOS è più semplice, che è possibile eseguire solo script alla fine del metodo AppDelegate.FinishedLaunching la situazione.

Sintesi vocale

Di seguito viene illustrato come aggiungere alcune funzionalità a CSCS, utilizzando la sintesi vocale come esempio.

In primo luogo, è necessario creare una classe che deriva dalla classe ParserFunction e ignorare il relativo metodo Evaluate virtuale protetto, come illustrato nella figura 5.

Figura 5 parlare di implementazione della funzione

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;
  }
}

Questa classe è un wrapper sull'implementazione di sintesi vocale effettivo. Per iOS, l'implementazione di sintesi vocale è illustrata nel figura 6 Android l'implementazione è simile, ma richiede un po' più di codifica. È possibile visualizzarlo nel download del codice sorgente associato.

Figura 6 iOS implementazione di sintesi vocale (frammento)

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);
    }
  }
}

Una volta ottenuto un'implementazione, è necessario collegarlo al parser. Questa operazione viene eseguita nel progetto condiviso nel metodo statico CommonFunctions.RegisterFunctions (come mostrato nella figura 3):

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

Riconoscimento vocale

Per il riconoscimento vocale è necessario utilizzare un callback di funzione per indicare all'utente di quali word è stato effettivamente riconosciuto (o per segnalare un errore, come in figura 2).

Ora di implementare due funzioni per il riconoscimento vocale, uno per avviare il riconoscimento vocale e un'altra per annullare l'operazione. Queste due funzioni sono registrate con il parser come sintesi vocale è registrato nella sezione precedente:

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

Viene illustrata l'implementazione di queste due funzioni per iOS in figura 7. Per Android l'implementazione è simile, ma si noti che il riconoscimento vocale sia stato aggiunto a iOS solo nella versione 10.0, è necessario controllare la versione del dispositivo e, se necessario, informare l'utente che il dispositivo non lo supporta nelle versioni precedenti di iOS 10.0.

Implementazione di riconoscimento vocale figura 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;
  }
}

Il codice di riconoscimento vocale effettivo è nella classe SST. È un po' troppo lungo da visualizzare qui e anche diverso per iOS e Android. Invito di estrarlo nel codice sorgente associato.

Non contiene una funzione di callback sintesi vocale, ma è possibile aggiungere uno in modo analogo, per informare l'utente quando viene completato il riconoscimento vocale (o se si verifica un errore). Il callback al codice CSCS viene eseguito richiamando il metodo 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;
}

È possibile visualizzare la modalità con cui questa funzione viene utilizzata figura 7.

Esempio: Conversione di valuta

Ad esempio di utilizzo di diverse funzionalità CSCS per lo sviluppo di app multipiattaforma, creare un'app da zero, una conversione di valuta.

Per ottenere i tassi di cambio aggiornati nell'app, è necessario utilizzare un servizio online. Si è scelto exchangerate api.com. Il sito fornisce un servizio Web da usare in cui le prime 1.000 richieste al mese sono gratuite, che dovrebbe essere sufficiente per avviare. Dopo la registrazione ottenere una chiave univoca, che è necessario fornire a ogni richiesta.

L'applicazione dispone di visualizzazioni diverse in modalità verticale e orizzontale. Figura 8 Mostra orientamento verticale e figura 9 Mostra orizzontale per iPhone e Android.

Conversione di valuta in modalità verticale su iPhone (a sinistra) e in Android (a destra)
Conversione di valuta nella figura 8 in modalità verticale su iPhone (a sinistra) e in Android (a destra)

Conversione di valuta in modalità orizzontale su iPhone (superiore) e in Android (inferiore)
Conversione di valuta nella figura 9 in modalità orizzontale su iPhone (superiore) e in Android (inferiore)

Figura 10 contiene l'intera implementazione CSCS dell'app di conversione di valuta.

Figura 10 CSCS implementazione dell'App di conversione valuta

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_refresh e on_about funzioni sono entrambi i callback che si verificano quando l'utente fa clic su un pulsante.

Il metodo on_about viene eseguito quando l'utente fa clic sul pulsante "Con" nella scheda Impostazioni, provocando la funzione OpenUrl aprire la exchangerate api.com home page nel browser predefinito (in questa scheda non viene visualizzata nella Figura 8 e figura 9). Il metodo on_refresh viene eseguito quando l'utente fa clic sul pulsante Convert. Si otterranno le valute selezionate e viene richiamata la funzione currency_request CSCS, che esegue la conversione di velocità effettiva.

La funzione currency_request controlla innanzitutto se entrambe le valute sono uguali, in questo caso sappia che la velocità è 1 e non è necessario chiamare un servizio Web (si desidera salvare il libero utilizza limitato di questo servizio al mese). In caso contrario, viene chiamata la funzione di WebRequest. Questa funzione è comune a entrambi iOS e Android e la relativa implementazione sia visualizzato in figura 11. Si noti che non è necessario eseguire la gestione delle eccezioni nel codice c#. Se viene generata un'eccezione (ad esempio, se il servizio è disponibile), l'eccezione verrà propagata al codice CSCS, in cui venga intercettato. Si noti inoltre che la funzione di WebRequest viene implementata in modo sincrono. Si può inoltre rendere asincrona specificando la funzione di callback da chiamare quando viene eseguita la richiesta (analoga alla funzionalità di riconoscimento vocale illustrato in precedenza).

Figura 11 c# attuazione di WebRequestFunction valutare (metodo)

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);
  }
}

Continuare l'analisi codice CSCS in figura 10. È dato dal fatto che descrive cosa accade nella funzione currency_request. La risposta JSON ottenere da exchangerate api.com sarà simile alla seguente:

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

Il timestamp è il numero di secondi trascorsi dal 1 gennaio 1970. La funzione CSCS Timestamp(format) converte questo numero di secondi in un formato di ora o la data specificata.

StrBetween (dati, strStart, strEnd) è una comoda funzione per estrarre una sottostringa dalla stringa di dati tra stringhe strStart1 e strStart2.

Dopo aver estratto la frequenza, data e ora, è impostato per le etichette corrispondenti utilizzando la funzione SetText (widgetName, testo).

Nella funzione init inizializzare i dati, ed è possibile aggiungervi ulteriori valute per la conversione.

È facile layout diversi per gli orientamenti diversi: registrare i callback di modifica orientamento con la funzione RegisterOrientationChange. Le funzioni on_portrait e on_landscape vengono chiamate ogni volta che cambia l'orientamento del dispositivo. Come si può vedere nella parte inferiore della figura 10, viene impostato tramite la chiamata:

RegisterOrientationChange("on_portrait", "on_landscape");

In generale è aggiungere widget sullo schermo in particolari posizioni, utilizzando la logica illustrato nell'esempio "Hello, World!" nella prima sezione. È possibile notare gli sfondi telefono diverso per la modalità verticale e orizzontale. Questa operazione viene eseguita utilizzando la funzione SetBackground(imageName) CSCS.

Funzione AddOrSelectTab ha la firma seguente:

AddOrSelectTab(Tab_Name, Picture_when_active, Picture_when_inactive);

Se la scheda non esiste ancora, verrà aggiunto, in caso contrario verrà selezionato e tutti i widget consecutivi verranno aggiunti a questa scheda. Figura 12 viene illustrato come l'aspetto delle schede in modalità sia attiva e inattiva.

Attivo e inattivo schede in iOS
Figura 12 attivo e inattivo schede in iOS

Conclusioni

In questo articolo è emerso che, con CSCS è possibile programmare le App per dispositivi mobili utilizzando un linguaggio di scripting. Lo script viene convertito in codice nativo usando l'interprete c# e l'infrastruttura di Xamarin. Gli script CSCS eseguire qualsiasi operazione che può essere eseguito nel linguaggio c# (e in Xamarin c# di eseguire qualsiasi operazione che si possono eseguire attività di sviluppo di app nativa).

Già pubblicati un'app scritta interamente in CSCS. Estrarre la versione di iOS in apple.co/2yixGxZ e la versione di Android in goo.gl/zADtNb.

CSCS scripting App per dispositivi mobili è lontano dal completo. Per aggiungere nuove funzionalità a CSCS, si crea una nuova classe che deriva dalla classe ParserFunction ed eseguire l'override di metodo di valutazione. Quindi registrare tale classe con il parser, specificando il nome CSCS:

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

CSCS è possibile inserire tutti i widget predefiniti a livello di codice, e lo stesso codice verrà utilizzato per Android e iOS. E non è necessario utilizzare qualsiasi XAML a tale scopo, come si farebbe con xamarin. Forms.

È anche possibile combinare CSCS con il codice c# esistente, è facile chiamare il codice c# da CSCS, come illustrato in codemag.com/article/1711081. È inoltre possibile verificare l'elenco di funzioni implementate in CSCS dell'articolo. Ma per la versione più recente, più aggiornate CSCS funzioni e funzionalità, visitare github.com/vassilych/mobile.

Purtroppo, non è disponibile spazio per illustrare alcuni altri sporadico operazioni da eseguire in CSCS, ad esempio l'acquisto in-app e gli annunci di fatturazione, nell'applicazione, alla pianificazione di eventi monofase e ricorrenti e altre informazioni, ma è possibile estrarre nel codice sorgente associato scaricare.

Vassili Kaplanè uno sviluppatore di Microsoft Lync precedente. Ed è quindi appassionato di programmazione in c#, C++, Python e ora in CSCS. Attualmente vive in Zurigo, Svizzera e funziona come freelance per diversi gruppi. È possibile contattarlo in iLanguage.ch

Grazie per il seguente esperto tecnico di Microsoft per la revisione dell'articolo: James McCaffrey
Dr. James McCaffrey funziona per Microsoft Research Redmond, WA Ha lavorato su diversi prodotti Microsoft, tra cui Internet Explorer e Bing. Dr. McCaffrey può essere raggiunto al jamccaff@microsoft.com.


Viene illustrato in questo articolo nel forum di MSDN Magazine