Difference between revisions of "CSharp:Android App - primi step"
From Aino Wiki
(→Autorizzazioni\Permessi) |
(No difference)
|
Latest revision as of 09:20, 5 March 2022
Contents
Cenni teorici
Per sviluppare applicazioni destinate ai dispositivi Android usando Microsoft Visual Studio, si usa la tecnologia Xamarin (dall'omonima azienda acquistata da Microsoft nel 2016).
Lo sviluppo di un'applicazione con Xamarin può avvenire utilizzando due approcci differenti chiamati Xamarin Native e Xamarin.Forms.
Con il termine Xamarin Native, si indica lo sviluppo di codice specifico per una singola piattaforma, Android (Xamarin.Android) o alternativamente iOS (Xamarin.iOS), entrambe richiedono specifiche librerie quindi specifici SDK.
Xamarin.Android
e Xamarin.iOS
sono entrambi progettati sulla base di Mono, una
implementazione open source di Microsoft .NET Framework basata sugli standard aperti .NET ECMA.
I due approcci Xamarin Native e Xamarin.Forms però non sono mutuamente esclusivi.
Un'applicazione è realizzabile in gran parte utilizzando Xamarin.Forms ed è possibile intervenire solo in specifici punti con Xamarin Native dove richiesto. In questo modo si possono ottenere i vantaggi di ciascun approccio.
Xamarin Native
In Android il compilatore di Xamarin consente di compilare nel linguaggio intermedio (IL), che viene quindi compilato Just-in-Time (JIT) in assembly nativo all'avvio dell'applicazione.
In iOS invece, il compilatore Ahead Of Time (AOT) di Xamarin compila le applicazioni Xamarin.iOS direttamente nel codice assembly ARM nativo.
In entrambi i casi, il risultato è indistinguibile da quello prodotto con gli ambienti di sviluppo predefiniti delle due piattaforme e quindi i pacchetti APK e IPA potranno essere distribuiti negli store e nei dispositivi esattamente allo stesso modo.
Xamarin Forms
Xamarin.Forms invece, lavora al di sopra di Xamarin.Android e Xamarin.iOS e permette di realizzare interfacce utente multipiattaforma in C# e XAML (Extensible Application Markup Language).
Tramite questo approccio, viene offerto allo sviluppatore un sottoinsieme delle API comune alle diverse piattaforme richiesto per scrivere la gran parte di un'app in una codebase unificata.
Materiale
- Microsoft DOC
- Sviluppare App con Google DRIVE developers.google.com
- Salvataggio file in locale docs.microsoft.com
- Gestione dei file docs.microsoft.com
Setup iniziale
Si comincia col dire che l'approccio presentato in questa pagina è "Xamarin Native".
Supponendo di usare Visual Studio 2019 e che sia già installato, occorre che si sia aggiunto anche il pacchetto "Mobile development with .NET" con Xamarin:
Per eseguire il simulatore occorre abilitare le seguenti funzionalità di Windows:
ed anche:
Applicazioni
Creazione nuovo progetto
Particolare della User Iterface principale e del punto di ingresso della applicazione:
Codice
Messaggi di dialogo e popup
- Guida docs.microsoft.com
- dare una occhiata qui per le finestre di dialogo: stacktips.com
Per visualizzare un semplice messaggio che scompare dopo il tempo predefinito nella variabile ToastLength.Long
:
Toast.MakeText(ApplicationContext, "Ciao ciao", ToastLength.Long).Show();
Oppure, se inserito in un handler dove il sender è istanziato con la vista corrente:
View view = (View)sender; Snackbar.Make(view, "Messaggio cucu", Snackbar.LengthShort).Show();
Anche in questo caso il messaggio è a tempo, Snackbar.LengthShort
, ma ci sono più opzioni ad esempio per non farlo sparire basta: Snackbar.LengthIndefinite
Per visualizzare il classico messaggio di dialogo:
using AlertDialog = Android.App.AlertDialog; //... AlertDialog.Builder dialog = new AlertDialog.Builder(this); AlertDialog alert = dialog.Create(); alert.SetIcon(Android.Resource.Drawable.IcDialogInfo); alert.SetTitle("Alert"); alert.SetMessage("Ciao questo è un messaggio che richiede attenzione"); alert.SetButton("OK", (c, ev) => { }); alert.Show();
Accesso ai files
Android raggruppa il file System in due tipi diversi di archiviazione:
- Archiviazione interna – si tratta di una parte del file system a cui è possibile accedere solo dall'applicazione o dal sistema operativo.
- Archiviazione esterna – si tratta di una partizione per l'archiviazione di file accessibile da tutte le app, dall'utente e possibilmente da altri dispositivi. In alcuni dispositivi, l'archiviazione esterna può essere rimovibile (ad esempio una scheda SD).
Archiviazione Interna
Archiviazione interna si riferisce alla memoria che Android alloca al sistema operativo, apk e per le singole app.
Questo spazio non è accessibile ad eccezione del sistema operativo o delle app.
Android alloca una directory nella partizione di archiviazione interna per ogni app.
In Android 6,0 o successiva, il backup dei file nell'archiviazione interna può essere eseguito automaticamente da Google usando la funzionalità di backup automatico developer.android.com.
L'archiviazione interna presenta gli svantaggi seguenti:
- I file non possono essere condivisi.
- I file verranno eliminati quando l'app viene disinstallata.
- Lo spazio disponibile nell'archiviazione interna potrebbe essere limitato.
Per l'archiviazione interna si usano degli helper della libreria System Novell.Essentials
, DOC
// ARCHIVIAZIONE INTERNA // ES.: /data/user/0/com.companyname.phoneword/files/ProvaTesto.txt string storePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); string testFileName = "ProvaTesto.txt"; TxtV_HelpDebug.Text = Path.Combine(storePath, testFileName);
Archiviazione Esterna
Eaiatono due tipo diversi di file per l'archiviazione esterna:
- file privati, sono file specifici dell'applicazione. Android li archivia in una directory specifica. Tali file sono ancora visibili e accessibili da altre app nel dispositivo, non sono sotto la protezione speciale di Android.
- file pubblici, sono file che non specifici dell'applicazione e che devono essere condivisi liberamente.
//Esempio di radice percorso ARCHIVIAZIONE ESTERNA: // //storage/emulated/0/ // //Segue codice accesso archiviazione esterna mediante codice string tmpArchivePath = string.Empty; Java.IO.File extStorage = Android.OS.Environment.ExternalStorageDirectory; Java.IO.File dir = new Java.IO.File(extStorage.AbsolutePath); tmpArchivePath = dir.AbsolutePath; //storage/emulated/0/
Permessi \ Autorizzazioni
Android considera l'accesso a una risorsa di archiviazione esterna come una attività pericolosa per cui occorre preventivamente un'autorizzazione esplicita di accesso. Occorre modificare opportunamente il file AndroidManifest.xml
. Per maggiori dettagli sui permessi leggere docs.microsoft.com
Oppure procedere come segue ceccando il flag come segue dopo aver premuto il tasto dx del mouse sul progetto dell'applicazione:
Quindi aggiungere anche la possibilità di leggerli, un AndroidManifest.xml
tipo potrebbe essere:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="it.aino.bankpassword" android:installLocation="auto"> ... <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> </manifest>
Oltre a quanto elencato occorre anche che l'utente dia un consenso esplicito al primo avvio dell'applicazione questo è necessario dalla build version del SDK API >= 23 (ottenibile con il codice: int sdkApi_info = (int)Build.VERSION.SdkInt;
). Esiste del codice riusabile per effettuare questa verifica e richiedere l'intervento dell'utente:
using Android.Support.V4.Content; using Android.Content.PM; using Android; using Android.Support.V4.App; //... bool isAccessDenied = false; if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted) { TxtV_HelpDebug.Text += "\r\nNON Si dispone dei permessi di LETTURA per l'archivio esterno"; isAccessDenied = true; } if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted) { TxtV_HelpDebug.Text += "\r\nNON Si dispone dei permessi di SCRITTURA per l'archivio esterno"; isAccessDenied = true; } if (isAccessDenied) { var permissions = new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }; ActivityCompat.RequestPermissions(this, permissions, 9); // RequestCode REQUEST_FOLDER_PERMISSION = 9? }
Il codice di prima, dopo l'intervento esplicito dell'utente, genera l'evento OnRequestPermissionsResult
il cui override si presume nella MainActivity.
// ARCHIVIAZIONE ESTERNA // ES.: / Java.IO.File sdCard = Android.OS.Environment.ExternalStorageDirectory; Java.IO.File dir = new Java.IO.File(sdCard.AbsolutePath); string storePath = dir.AbsolutePath; // storage/emulated/o/
Handlers Eventi
Esempio di codice associato al primo evento all'avvio della applicazione, OnCreate
. Si intercettano gli oggetti della UI e si associa del codice come Handler.
namespace PhoneWord { [Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)] public class MainActivity : AppCompatActivity { static readonly List<string> g_phoneNumbers = new List<string>(); protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); SetContentView(Resource.Layout.activity_main); Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar); SetSupportActionBar(toolbar); FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab); fab.Click += FabOnClick; // --- Il mio codice qui // Get our UI controls from the loaded layout EditText phoneNumberText = FindViewById<EditText>(Resource.Id.PhoneNumberText); TextView translatedPhoneWord = FindViewById<TextView>(Resource.Id.TranslatedPhoneword); Button translateButton = FindViewById<Button>(Resource.Id.TranslateButton); Button translationHistoryButton = FindViewById<Button>(Resource.Id.TranslationHistoryButton); // Add code to translate number translateButton.Click += (sender, e) => { // Translate user's alphanumeric phone number to numeric string translatedNumber = Core.PhonewordTranslator.ToNumber(phoneNumberText.Text); if (string.IsNullOrWhiteSpace(translatedNumber)) { translatedPhoneWord.Text = string.Empty; } else { translatedPhoneWord.Text = translatedNumber; g_phoneNumbers.Add(translatedNumber); translationHistoryButton.Enabled = true; } }; translationHistoryButton.Click += (sender, e) => { var intent = new Intent(this, typeof(TranslationHistoryActivity)); intent.PutStringArrayListExtra("phone_numbers", g_phoneNumbers); StartActivity(intent); }; } //.... }
Altra via diversa dalla precedente per associare un handler ad un evento legato ad un oggetto della UI.
//... protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); SetContentView(Resource.Layout.activity_main); //... FloatingActionButton BtnMioBottoneAzione = FindViewById<FloatingActionButton>(Resource.Id.fab); BtnMioBottoneAzione.Click += FabOnClick; } private void FabOnClick(object sender, EventArgs eventArgs) { View view = (View) sender; Snackbar.Make(view, "Replace with your own action", Snackbar.LengthLong) .SetAction("Action", (Android.Views.View.IOnClickListener)null).Show(); }
Multi activities
- Tutorial stacktips.com
- Tutorial Nuova Activity e Pagina: c-sharpcorner.com
Per creare una ulteriore Activity (da aggiungere alla MainActivity), tasto dx sul progetto e cliccare su "Add", poi "New Item", quindi "Activity" (aggiungerà una classe: "MiaActivity.cs").
Ora per lanciare l'Activity basta andare all'handler del evento dell'interfaccia che si vuole faccia aprire l'activity ed aggiungere il seguente codice:
StartActivity(typeof(MiaActivity));
Se invece si voglio passare dei dati occorre creare una Intent
a cui si aggiungono i dati nella forma (CHIAVE, VALORE):
Intent intent = new Intent(this, typeof(MiaActivity)); intent.PutExtra("Campo1", "Valore, Il mio nome"); intent.PutExtra("Campo2", 1001); StartActivity(intent);
ovvero all'oggetto Intent si aggiungono i dati con il metodo .PutExtra("CHIAVE", VALORE);
. Così si posson passare solo dati di tipo primitivo.
Doc:
Nella Activity "MiaActivity" i dati passati si recuperano così:
string nome= Intent.GetStringExtra("Campo1"); int valore = Intent.GetIntegerExtra("Campo2");
Per ricevere indietro dati da una Activity aperta (MiaActivity), si usera invece l'istruzione startActivityForResult()
quindi per ottenerne i valori si usa una CallBack creando il metodo onActivityResult()
.
- Doc docs.microsoft.com
- Switch tra viste stackoverflow.com
Xamarin.Forms
supporta la navigazione tra oggetti incastonati (navigazione gerarchica):
-
NavigationPage
, dove la prossima pagina scivola dentro, -
TabbedPage
, ? -
CarouselPage
, che consente di spostarsi avanti ed indietro tra pagine.
On top of this, all pages also supports PushModalAsync()</code> which just push a new page on top of the existing one.
Se hai bisogno che l'utente non possa tornare indietro alla pagina precedente (usando gesture o il bottone hardware per tornare indietro), puoi mantenere la stessa pagina visualizzata e sostituirla semplicemente nel suo Content
.
The suggested options of replacing the root page works as well, but you'll have to handle that differently for each platform.
Lista ListView
Il seguente è in una griglia (GridLayout) 9x3
<ListView android:minWidth="25px" android:minHeight="25px" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/LVMatchItems" android:layout_row="6" android:layout_column="0" android:layout_columnSpan="3" />
Lato codice:
//.. LVMatchItems = FindViewById<ListView>(Resource.Id.LVMatchItems); // Associo un handler: LVMatchItems.ItemClick += LVMatchItems_ItemClick; //Metodo di popolamento con un array stringa LVMatchItems.Adapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItem1, m_arrStrFilteredItem ?? new string[0]); //Metodo Handler: private void LVMatchItems_ItemClick(object sender, AdapterView.ItemClickEventArgs e) { var t = m_arrStrFilteredItem[e.Position]; Toast.MakeText(this, t, ToastLength.Long).Show(); }
Librerie
Xamarin.Android.Support.Design
- Doc, articolo devblogs.microsoft.com
Collaudo
E' possibile creare un emulatore di dispositivo ("Android Emulator", immagine del cellulare che segue), questo sarà per default il depositario dell'applicazione in sviluppo (ciò dopo opportune impostazioni e configurazioni).
Per analizzare le risorse dell'emulatore (ad es. il suo FileSystem) è possibile usare l'applicativo "Android Device Emulator". Nella seguente immagine è possibile prendere visione di una configurazione tipo.
Risorse
- Icone, sito ufficiale: material.io
Risuluzione di Problemi
Deploy fallisce
application deploy on Android 11 fails "ADB1000: deployment failed operation not permitted"
- Tasto dx sul progetto
- Tab "Android Options"
- togliere il check su "Use Fast Deployment (debug mode only)"
Mappa e Link
C# | Android App | XAMARIN Tips | Appunti
Visual Studio | Windows Form | MS SQL | Dizionario
Parole chiave: