Come creare una bussola per Android: MobileOSApp

In questo articolo vedremo come realizzare una bussola creando una piccola applicazione per Android. Lo scopo principale non sarà quello di realizzare una app curata graficamente o con un numero esagerato di opzioni ma quello di capire il funzionamento del framework attraverso le sue API.

compass_img

I punti di interesse di questo articolo saranno i seguenti:

  • Gestione delle risorse immagini nella classe R.java e xml
  • Utilizzo del file string.xml per la configurazione delle risorse
  • Utilizzo dei sensori per il monitoraggio dei gradi di rotazione
  • Aggiunta di Listener per l’esecuzione di operazioni su specifici eventi
  • Uso dei componenti grafici ImageView, Spinner e TextView

Tutto quello che la nostra app sarà in grado di fare sarà quello di visualizzare l’immagine della bussola facendola ruotare grazie ai sensori di rotazione in modo che il nord sia sempre orientato e visualizzare uno spinner per la scelta dell’immagine da applicare.
Abbiamo già visto in precedenza come creare un nuovo progetto Android e una volta completato il wizard possiamo iniziare a mettere mano al codice.

Gestione delle immagini.

Come prima operazione dovremo scegliere alcune immagini per visualizzare la nostra bussola. Nel mio esempio ho utilizzato 4 immagini diverse che, come vedremo più avanti, potrò selezionare dinamicamente. Il nome del file dovrà rispettare il pattern [a-z0-9_.] quindi una stringa alfanumerica senza caratteri speciali. Una volta deciso quali immagini utilizzare dovremo andare a copiarle nella cartella drawable ( io ho scelto la drawable-hdpi ). Se l’operazione verrà effettuata attraverso l’editor di eclipse, verrà aggiornato in automatico anche il file R.java che altrimenti dovremo editare a mano. Ecco cosa viene aggiunto al file relativamente alle immagini che abbiamo scelto:

[java gutter=”off”]
public static final class drawable {
public static final int compass=0x7f020000;
public static final int compass2=0x7f020001;
public static final int compass3=0x7f020002;
public static final int compass4=0x7f020003;
public static final int ic_launcher=0x7f020004;
}
[/java]

Vedremo in seguito in che modo possono essere utilizzate queste definizioni per accedere alle nostre immagini.

Uno sguardo al file xml dell’activity principale.

ImageView
Per poter visualizzare l’immagine dobbiamo avvalerci del componente ImageView da aggiungere nell’xml dell’activity. Se osserviamo la porzione di codice nel file, quello che ci interessa è questa:

[xml highlight=”2,9,10″]
<ImageView
android:id="@+id/imageViewCompass"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/aboutTextView"
android:layout_below="@+id/spinnerImages"
android:layout_centerHorizontal="true"
android:layout_centerInParent="false"
android:src="@drawable/compass"
android:contentDescription="@string/compass_image_description" />
[/xml]

Prestiamo particolare attenzione alle righe:
2. viene vefinito l’id del componente, “imageViewCompass”.
9. indica la sorgente dell’immagine. “@drawable” rappresenta la classe che abbiamo visto prima nel file R.java e “compass” la sua costante. In questo modo il componente caricherà e visualizzerà l’immagine “compass”.
10. il contentDescription è facoltativo ma consigliato. Rappresenta una descrizione dell’immagine.

Spinner
Lo spinner non è altro che il componente che ci permetterà attraverso un menu a tendina di poter selezionare una diversa immagine per la bussola. Il codice che lo descrive nel file è questa.

[xml highlight=”2″]
<Spinner
android:id="@+id/spinnerImages"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
[/xml]

L’unica riga degna di nota è come per l’ImageView la numero 2 dove assegnamo un id univoco al componente.

TextView
Quest’ultimo componente non è altro che una casella di testo non modificabile dove possiamo inserire qualsiasi tipo di testo.

[xml highlight=”8,9″]
<TextView
android:id="@+id/aboutTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:autoLink="web"
android:text="@string/about_text"
android:textSize="12sp" />
[/xml]

Ho inserito questo componente per focalizzare l’attenzione su 2 aspetti importanti. Uno è quello che troviamo alla riga 8, cioè l’attributo “web” che permette di riconosce in automatico la presenza di un indirizzo web e fare in modo che venga gestito il link su di esso. L’altro è quello presente alla riga 9. La chiave “android:text” ci permette di specificare il testo da visualizzare e questo può essere fatto in 2 maniere:
– aggiungendo il testo come valore dell’attributo. Es: android:text=”www.MobileOS.it – fattazzo”
– utilizzando il file string.xml per la sua gestione. Es: android:text=”@string/about_text”

Il file string.xml.

Lo scopo di questo file è quello di gestire tutte le risorse di testo con eventuali stili e formattazioni. Per chi è pratico di programmazione può essere paragonato ai file properties dove troviamo il formato chiave=valore. Vediamo di analizzare il nostro file per dare una spiegazione pratica.

[xml]
<string name="app_name">MobileOS Compass App</string>
<string name="about_text">www.MobileOS.it – fattazzo</string>

<string name="compass_image_description">Compass image</string>

<string-array name="compass_array">
<item>compass</item>
<item>compass2</item>
<item>compass3</item>
<item>compass4</item>
</string-array>
[/xml]

Le risorse utilizzabili in questo file sono String, String Array e Quantity String. Nel caso caso precedente della TextView abbiamo deifinito il valore dell’attributo “android:text” con la stringa “@string/about_text”. Questo stà ad indicare che il testo da utilizzare dovrà essere caricato dal valore dell’elemento “about_text” del file string.xml.
Analogo è l’utilizzo dell’elemento string-array dove invece di definire una sola stringa verranno elencate tutte le voci che compongono l’array. L’esempio in questo caso ci è dato dall’elemento “compass_array” che contiene l’elenco dei nomi delle immagini da poter selezionare. Vedremo poi come utilizzarlo nella nostra spinner.

Le classi presenti.

Non mi soffermo a spiegare il codice delle 3 classi in quanto ho inserito dei commenti abbastanza chiari. Vorrei invece descriverne il funzionamento. La MainActivity è la classe principale e alla sua creazione si occupa fondamentalmente di aggiungere alla spinner e al manager dei sensori i listener che verranno attivati al cambio di selezione nel primo caso e al cambio di rotazione del secondo caso. Da notare il caricamento delle voci della spinner attraverso “compass_array” definito nel file string.xml.
SpinnerImagesItemSelectionListener: questo listener, al cambio di selezione, ottiene la voce selezionata e attraverso la reflection la cerca all’interno dei drawable del file R.java per poi applicarla alla ImageView.
CompassSensorEventListener: registrato al cambio di rotazione, ottiene dall’evento il nuovo angolo di rotazione e crea un’animazione per ruotare l’immagine da quello vecchio a quello attuale.

[java tabsize=”4″ smarttabs=”true” title=”MainActivity.java” collapse=”true”]

public class MainActivity extends Activity {

@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// applica il layout dell’activity
setContentView(R.layout.activity_main);

// ottengo il controllo che visualizza la scelta delle immagini da usare
Spinner spinnerImages = (Spinner) findViewById(R.id.spinnerImages);

// creo l’ArrayAdapter che contiene tutte le scelte possibili che vengono caricare dall’array
// definito nel file strings.xml
ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.compass_array,
android.R.layout.simple_spinner_item);
// Vado a definire che l’adapter si presenterà in forma di drop down list
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

// Applico l’adapter al controllo
spinnerImages.setAdapter(adapter);

// Aggiungo il listener che si attiva sul cambio di selezione
spinnerImages.setOnItemSelectedListener(new SpinnerImagesItemSelectionListener(this));

// ottengo il servizio dei sensori di android
SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

// registro la main activity stessa come listener al sensor manager
// Sensor.TYPE_ORIENTATION risulta ora deprecato perchè anzichè i sensori di orientamento dovrebbero
// essere usati in coppia quelli dell’accelerometro e magnetici. Su alcuni dispositivi
// questo non sembra ancora funzionare quindi lascio la vecchia implementazione
mSensorManager.registerListener(new CompassSensorEventListener(this),
mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME);
}
}

[/java]
[java tabsize=”4″ smarttabs=”true” title=”CompassSensorEventListener.java” collapse=”true”]
public class CompassSensorEventListener implements SensorEventListener {

// rappresenta i gradi attuali
private float gradi = 0f;

// image view che visualizza l’immagine della bussola
private ImageView imageCompass;

public CompassSensorEventListener(MainActivity mainActivity) {
super();

// ottengo il controllo che visualizza l’immagine
imageCompass = (ImageView) mainActivity.findViewById(R.id.imageViewCompass);
}

@Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
}

@Override
public void onSensorChanged(SensorEvent event) {

// angolo di rotazione attuale
float degree = Math.round(event.values[0]);

// creo la rotazione che dovrà fare immagine per portarsi dai gradi precedenti a quelli attuali
RotateAnimation ra = new RotateAnimation(gradi, -degree, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);

// durata dell’animazione
ra.setDuration(200);

// una volta terminata l’animazione, questa viene applicata all’immagine
ra.setFillAfter(true);

// faccio partire l’animazione
imageCompass.startAnimation(ra);

// memorizzo i gradi attuali
gradi = -degree;
}

}
[/java]
[java tabsize=”4″ smarttabs=”true” title=”SpinnerImagesItemSelectionListener.java” collapse=”true”]
public class SpinnerImagesItemSelectionListener implements OnItemSelectedListener {

private ImageView imageCompass;

public SpinnerImagesItemSelectionListener(MainActivity mainActivity) {
imageCompass = (ImageView) mainActivity.findViewById(R.id.imageViewCompass);
}

@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long arg3) {

// recupero il nome dell’immagine selezionata
String imageName = (String) parent.getItemAtPosition(pos);

R.drawable ourRID = new R.drawable();
Field photoNameField;
try {
// attraverso la reflection ottengo il field dell’immagine
photoNameField = ourRID.getClass().getField(imageName);

// applico l’immagine ottenendo l’id del filed
imageCompass.setImageResource(photoNameField.getInt(ourRID));
} catch (Exception e) {
// Eccezione sollevata in caso non venga trovata l’immagine o
// durante l’ottenimento del suo id
e.printStackTrace();
}
}

@Override
public void onNothingSelected(AdapterView<?> arg0) {
}

}
[/java]

Come è possibile notare il codice è molto semplice e con poche righe abbiamo realizzato una semplice e funzionale applicazione. In fondo all’articolo lascio i link dell’app (file .apk) se qualcuno è interessato a provarla e il link per scaricare il progetto in modo tale che chiunque possa provarlo magari implementando nuove funzionalità per esercitarsi. In esso troverete tutti i sorgenti e le immagini che ho utilizzato per visualizzare la bussola.
La compatibilità dell’applicazione dovrebbe essere da Android 2.2 alla 4.4.2. Purtroppo sono riuscito a testare solo le versioni 4.3 e 4.4.2 ma non credo ci potranno essere problemi dato che ho utilizzato funzioni standard.
Per provare questa applicazione dovrete abilitare le sorgenti sconosciute ( dal menu Impostazioni –> Sicurezza –> Sorgenti sconosciute ) dato che ho utilizzato una chiave auto generata per firmare l’applicazione ma non temete, la mia app non è un virus :).

MobileOS Compass App

MobileOS Compass App

MobileOS Compass App (sorgente)

MobileOS Compass App (sorgente)













Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *