Generare un colore randomico di tonalità "pastello"

Questo script restituisce il codice di un colore generato a caso nelle tonalità pastello.

La generazione viene definita attraverso le 3 caratteristiche "hue", "saturation" e "brightness".

Poi utilizza la funzione HSVToColor.

I tre parametri sono definiti in questo modo:

HUE

E' un valore intero tra 0 e 360 (rappresenta i "gradi" nel cerchio di selezione dei colori), che però viene passato come float.

SATURATION

E' un float tra 0 e 1, cioè un valore percentuale. Più è alto, più il colore è "carico".

BRIGHTNESS

E' un float tra 0 e 1, cioè un valore percentuale. Più è alto, più il colore è "chiaro".

Questo è il codice:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private String getRandomColor()
{
    // variabili di appoggio
    int newColor;
    Random rnd = new Random();

    // generazione di HUE, SATURATION e BRIGHTNESS
    int hue = (int) Math.floor(0 + rnd.nextDouble()*(360));
    int saturation = (int) Math.floor(0 + rnd.nextDouble()*(40));
    int brightness = (int) Math.floor(70 + rnd.nextDouble()*(30));

    // HSVToColor ( [0..360], [0..1], [0..1])
    newColor = android.graphics.Color.HSVToColor(new float[] {((float) hue), ((float) saturation) / 100, ((float) brightness) / 100});

    return String.format("#%06X", (0xFFFFFF & newColor));

} // getRandomColor

Camera e autofocus

Gestire l'autofocus della camera è veramente una bazzecola, basta impostare un parametro.

Ecco come:

1
2
3
4
5
6
7
8
mCamera = Camera.open(0);

//set camera to continually auto-focus
Camera.Parameters params = mCamera.getParameters();

params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

mCamera.setParameters(params);

Android Virtual Devices: cambiare il percorso di destinazione

Quando ho installato Windows 7 sul mio PC ho deciso di creare due partizioni, una per il sistema operativo ed una per i dati.
Purtroppo ho dedicato troppo poco spazio alla partizione di sistema, per cui spesso mi ritrovo a dover spostare file voluminosi tra le due partizioni.

I file che contengono gli AVD sono decisamente "pesanti" (nell'ordine di qualche giga), quindi ho provato a vedere se c'era un modo per trasferirli.

Ed è effettivamente possibile!

Nota: per effettuare lo spostamento, Android Studio dev'essere chiuso.

Procedura

Per prima cosa, bisogna individuare la directory in cui i file sono salvati. Il percorso di default è:

C:\Users\[nomeutente]\.android

Create una directory da qualche parte e spostateci dentro la directory .android.

Poi basta impostare una variabile d'ambiente.

Start Menu
Control Panel
System
Advanced System Settings (nel menu a sinistra)
Environment Variables

A questo punto inserite una nuova variabile utente:
nome variabile: ANDROID_SDK_HOME
valore: la directory di destinazione (escluso il .android)

A questo punto potete salvare ed aprire Android Studio. Ora il sistema caricherà i Virtual Device dalla nuova directory!

Salvare login e password sul dispositivo: SharedPreferences

Nel creare un'app "privata" appoggiata ad un web service online, ho dovuto scontrarmi con la gestione dell'autenticazione.

Una volta ottenuto il riconoscimento, ho deciso che l'app avrebbe dovuto salvare i dati (email e password) per poi presentarli alla successiva richiesta.

Gestione di dati

Android mette a disposizione un sistema per salvare ed utilizzare dati di configurazione all'interno dell'app: lo SharedPreference.

L'oggetto in questione punta ad un file contenente combinazioni chiave-valore e fornisce metodi per la lettura e la scrittura degli stessi. Questi file, gestiti dal framework, possono essere marcati come privati o come condivisi.

Tipologie di dati

Gli oggetti che possono essere salvati e recuperati dallo SharedPreference possono essere di vario tipo:
  • boolean
  • float
  • int
  • long
  • string

Dati immutabili

E' importante capire che gli oggetti ritornati dalle varie funzioni get devono essere trattati come immutabili dall'applicazione.

Creazione di uno SharedPreference

La creazione è abbastanza semplice: si richiama il metodo getSharedPreferences, a cui viene passato il nome del file da leggere e la modalità di operazione.

Questo è il codice:

1
SharedPreferences sp = getSharedPreferences("Login", MODE_PRIVATE);

Modalità di operazione

Queste sono le modalità operative legate agli SharedPreference:
  • MODE_PRIVATE
  • MODE_WORLD_READABLE
  • MODE_WORLD_WRITEABLE
  • MODE_MULTI_PROCESS
Da notare che gli ultimi tre sono stati dichiarati "deprecated", quindi l'unica opzione valida è la modalità "privata".

Salvare nuovi valori o aggiornare i valori presenti

Per mantenere la consistenza dei dati, è necessario utilizzare un editor. Una volta creato l'oggetto, bisogna quindi generare un SharedPreferences.Editor:

1
SharedPreferences.Editor spEd = sp.edit();

A questo punto si può inserire un nuovo valore (o aggiornare un valore già presente) con una delle funzioni put presenti.

Un esempio:

1
2
spEd.putString("email", field_email);
spEd.putString("password", field_password);

Il primo campo indica la chiave per il successivo recupero, il secondo campo invece è il contenuto da salvare / aggiornare.

Per eliminare un valore presente, invece, si richiama la funzione remove:

1
spEd.remove("email");

Applicare le modifiche

Una volta inseriti i nuovi valori o aggiornati quelli presenti, è necessario eseguire un commit per confermare le modifiche:

1
spEd.commit();

Lettura dei valori presenti

La lettura dei valori già salvati è anche più semplice, dato che non occorre generare un editor:

1
2
3
SharedPreferences spGet = this.getSharedPreferences("Login", MODE_PRIVATE);
String mail_recuperata = spGet.getString("email", null);
String pass_recuperata = spGet.getString("password", null);

Il secondo parametro passato a getString è il valore di default restituito nel caso in cui la voce non fosse presente nello SharedPreference.

Aggiunta di una ProgressBar orizzontale sotto la ActionBar

Durante lo sviluppo di un'app che si connetteva ad un servizio remoto, ho avuto la necessità di visualizzare un elemento che indicasse all'utente l'attività in background che il dispositivo stava effettuando.

All'inizio ho optato per un ProgressDialog. E' una buona opzione, ma nel caso siano presenti delle animazioni a seguito di un invio, il dialog "blocca" la visualizzazione delle stesse e risulta antiestetico.

Ho quindi optato per un sistema meno "invasivo": una barra orizzontale per segnalare il lavoro in background.

L'oggetto ProgressBar è semplice da implementare, lo è meno l'integrazione con una ActionBar personalizzata e "importata" nel layout tramite un'inclusione, come in questo esempio:

1
2
3
4
5
6
7
8
9
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="it.example.test_progressbar.MainActivity">

    <include layout="@layout/toolbar"/>

Impostazioni di base ed elementi necessari

Dopo aver creato un nuovo progetto "base" con una Activity vuota, per implementare la ProgressBar sono necessari i seguenti file (da creare o modificare):
  • activity_main.xml
  • toolbar.xml
  • AndroidManifest.xml
  • styles.xml
  • MainActivity.java

Modifiche XML

Partiamo dalle modifiche al design XML, che poi saranno le più "corpose".

AndroidManifest.xml

Il problema iniziale da affrontare è che la struttura "base" creata da Android Studio inserisce una ActionBar "basilare" direttamente dall'AndroidManifest attraverso questa voce:

1
2
3
<application
    ...
    android:theme="@style/AppTheme">

Per eliminare l'ActionBar, bisogna caricare specificare queste righe nel file styles.xml:

1
2
3
4
<style name="AppTheme.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

rimuovere la voce incriminata e sostituirla con questa:

1
2
3
<application
    ...
    android:theme="@style/AppTheme.NoActionBar">

activity_main.xml

L'XML dell'Activity principale richiede una sola modifica, l'aggiunta della toolbar contenuta nel file separato.

Basta aggiungere, sotto all'apertura del tag del ConstraintLayout inserito dal sistema, di questa riga:

1
<include layout="@layout/toolbar"/>

toolbar.xml

La toolbar di norma viene innestata all'interno di un CoordinatorLayout, che verrà mantenuto.
Il codice di partenza è questo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="it.tortellinux.diceroller.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <LinearLayout
        android:id="@+id/fragment_container"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="android.support.design.widget.AppBarLayout$ScrollingViewBehavior"
        >
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

Per gestire correttamente la ProgressBar vanno applicate alcune modifiche sostanziali.

Per prima cosa, va rimosso quello che Android "presenta" sotto la Toolbar, cioè un piccolo margine e l'ombra.
Questo perché la ProgressBar verrà visualizzata "sotto" l'ombra e l'effetto sarà... inguardabile.

Bisogna quindi impostare l'elevation ed il margine inferiore a 0dp:

1
2
3
4
5
<android.support.design.widget.AppBarLayout
    ...
    app:elevation="0dp"
    android:layout_marginBottom="0dp"
    />

Poi è necessario aggiungere un FrameLayout che conterrà la ProgressBar. La rappresentazione "base" della ProgressBar utilizza un bordo superiore ed uno, più piccolo, inferiore.

Questi verranno eliminati inserendo un altezza di 3dp al frame ed impostando un margine negativo (sì, si può) alla ProgressBar, oltre ad un'altezza fissa.

E' necessario, infine, impostare questo comportamento al frame:

1
2
3
4
<FrameLayout
    ...
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >

Questo impedisce alla Toolbar di "coprire" la ProgressBar.

Questo è il codice finale:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="it.tortellinux.test_progressbar.MainActivity">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="3dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <ProgressBar
            android:id="@+id/progress_spinner"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="7dp"
            android:layout_gravity="top"
            android:layout_marginTop="-3dp"
            android:indeterminate="true"
            android:visibility="visible"
            />
    </FrameLayout>

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:elevation="0dp"
        android:layout_marginBottom="0dp">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            />

    </android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>

MainActivity.java

Per l'implementazione della ProgressBar bastano le modifiche all'XML indicate qui sopra, solo che c'è bisogno di un sistema per renderla visibile o farla sparire.

Dopo aver recuperato l'oggetto attraverso la funzione findViewById, per impostare la visibilità della ProgressBar si utilizzano due semplici comandi.

Per visualizzarla:

1
progressBar.setVisibility(View.VISIBLE);

Per farla sparire:

1
progressBar.setVisibility(View.GONE);

L'effetto finale

Completati i vari passaggi, si ottiene questo effetto:

Come personalizzare il nome dell'APK prodotto da Android Studio

Di norma, quando viene generato un APK si ottiene un file dal nome standard, "app-release.apk" o "app-debug.apk", che poi di norma va modificato per evitare la sovrascrittura di altre app durante il caricamento nei dispositivi o anche solo per la conservazione.

La gestione della compilazione, nei progetti Android, è gestita dal gradle. Per modificare il nome del file generato, quindi, si deve aggiungere qualche riga al build.gradle dell'app (e non del progetto "generale").

Il file, di norma, appare così:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "it.test.example"
        minSdkVersion 23
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support:design:25.3.1'
    testCompile 'junit:junit:4.12'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
}

la sezione importante che andrà modificata, buildTypes > release, è:


1
2
3
4
5
6
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

All'interno della sezione release bisogna inserire un nuovo "elemento":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
applicationVariants.all { variant ->
    variant.outputs.each { output ->
        project.ext { appName = 'nomeApplicazione' }
        def newName = output.outputFile.name
        newName = newName.replace("app-", "$project.ext.appName-")
        newName = newName.replace("-release", "")

        output.outputFile = new File(output.outputFile.parent, newName)
    }
}

Ovviamente dovete sostituire 'nomeApplicazione' con il nome dell'app!

In questo modo, quando l'APK verrà generato, verrà prodotto con il nome selezionato e non più come "app-release.apk".

Icone per menu e altro - SVG e PNG

Il design attuale dei menu, basato su Material, adotta uno stile particolare: il nome della voce affiancato ad un'icona.

Un ottimo repository per icone, contenente sia quelle fornite di base da Google sia quelle create e caricate dagli utenti, è:


Alcuni esempi:

Generare un colore randomico di tonalità "pastello"

Questo script restituisce il codice di un colore generato a caso nelle tonalità pastello. La generazione viene definita attraverso le 3 ca...