Android
ZOOM SUR 2 POINTS IMPORTANTS
Les ressources et Le Manifest
Structure d’un projet Android
Fichiers générés et .jar Tests
Code source java
Ressources (images, texte, couleurs, layout)
Configuration application et projet
Librairies externes
Types de ressources
res/layout-fr animator/ XML pour les propriétés animations
anim/ XML descriptifs des tween animations (avec transitions) color/ XML pour la liste de couleurs
drawable/ Fichiers images (.png, jpg, gif) ou XML
mipmap/ Icone de lancement de différentes densités layout/ XML de description de layouts
menu/ XML de descriptions de menus
raw/ Lié aux InputStream : de données brutes (musiques, html…)
values/ Pour les valeurs simples : Chaines, entiers, couleurs…(strings, colors, dimens, styles…) xml/ Des fichiers XML utiles
res/drawable-hdpi
res/drawable-small
res/drawable-large res/values-fr-rFR-port
Accès aux ressources
A la compilation, un fichier R est créé contenant des références sur les ressources de l’application.
C’est à l’aide de ce fichier qu’on peut accéder au ressources depuis du code.
Aperçu du contenu du fichier R.java :
Depuis le code, on peut alors accéder aux différentes ressources grâce à leur identifiant et ce fichier R.
Par exemple, pour accéder à du texte défini dans strings.xml, on appelle : getString( R.string. section_format );
POURQUOI ANDROID ET PAS LE
WEB ?
Les spécificités Android : accéder aux capteurs
Nom Dimension
du vecteur Unité Sémantique Values[]
Accelerometer 3 m/s2 l'accélération Mesure de (gravité incluse)
[0] axe x [1] axe y [2] axe z
Gyroscope 3 Radian/seconde
Mesure la rotation en termes de vitesse autour de chaque axe
[0] vitesse angulaire autour
de x [1] vitesse angulaire autour
de y [2] vitesse angulaire autour
de z
Light 1 Lux Mesure de la
luminosité [0]valeur Magnetic_Field 3 µTesla
Mesure du champ magnétique
[0] axe x [1] axe y [2] axe z
Orientation 3 degrés Mesure l'angle entre le nord
magnétique
[0] Azimut entre l'axe y et le nord [1] Rotation autour de l'axe x
(-180,180) [2] Rotation autour de l'axe y
(-90,90)
Pressure 1 KPascal Mesure la
pression [0]valeur
Proximity 1 mètre
Mesure la distance entre l'appareil et un objet cible
[0]valeur
Temperature 1 Celsius Mesure la
température [0]valeur
http://mathias-seguy.developpez.com/tutoriels/android/utiliser-capteurs/
C’est différent du responsive Pourquoi ?
Quid du cross platform ?
Gestion des ressources graphiques
Dossier drawable-XXXX : images
• XXHDPI (ultra haute densité)
• XHDPI (très haute densité)
• HDPI (haute densité)
• MDPI (moyenne densité)
• LDPI (basse densité)
Toujours essayer d’avoir les ressources dans toutes les densités
• meilleurs rendus
• scaling consommateurs de ressources (risque de OutOfMemoryException)
http://developer.android.com/guide/practices/screens_support.html
Manifest.xml
Configuration globale cœur du projet : - déclaration des activités (écrans)
- paramètre des activités (par exemple : définir l’activité
d’accueil ou verrouiller une activité en mode paysage)
- déclaration des permissions de l’application (ici, par exemple,
nous aurons besoin de la permission d’accès à internet pour
charger les images)
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 package="com.example.android"
3 android:versionCode="1"
4 android:versionName="1.0">
5
6 <uses-sdk android:minSdkVersion="10" />
7
8 <application android:icon="@drawable/ic_launcher">
9 10 ...
11
12 </application>
13 </manifest>
dans build.gradle
Configuration générale
1 <uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />
2 <uses-permission android:name="android.permission.INTERNET" />
Permissions
Accès Internet et téléphonie
1 <activity android:name=".Activity1" android:label="@string/app_name">
2 <intent-filter>
3 <action android:name="android.intent.action.MAIN" />
4 <category android:name="android.intent.category.LAUNCHER" />
5 </intent-filter>
6 </activity>
1 <uses-feature android:name="android.hardware.bluetooth" />
2 <uses-feature android:name="android.hardware.camera" />
Gestion des accès
Gère l’accès au matériel (hardware requis)
Définit les composants de l’application ainsi que leurs propriétés (Activities, Services ...)
COMPOSANTS : WIDGETS
Concrètement en Android
Une interface utilisateur d’une activité correspond à une hiérarchie de vues (dérivées de la classe View dans le package android.view.View.)
Chaque vue correspond à un espace de la fenêtre d’activité et peut correspondre à une interaction utilisateur : ex. un bouton.
Vous pouvez utiliser des widgets "Widgets" existant pour concevoir votre IHM Vous pouvez aussi créér en sous classant des classes existantes vos propres vues.
Boutons
<?xml version="1.0" encoding="utf-8"?>
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/button_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_send"
android:onClick="sendMessage" />
/** Called when the user touches the button */
public void sendMessage(View view) { // Do something in response to button click }
Button button = (Button) findViewById(R.id.button_send);
button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) {
// Do something in response to button click }
});
<Button
android:id="@+id/button_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_send"
android:onClick="sendMessage"
style="?android:attr/borderlessButtonStyle" />
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_pressed"
android:state_pressed="true" />
<item android:drawable="@drawable/button_focused"
android:state_focused="true" />
<item android:drawable="@drawable/button_default" />
</selector>
Gestion du click
Bouton sans bord
Pour personnaliser le fond
Exemples de WIDGETS
Gestion du temps Formulaire
Data Time Picker
Spinner
Spinner
<Spinner
android:id="@+id/planets_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="planets_array">
<item>Mercury</item>
<item>Venus</item>
<item>Earth</item>
<item>Mars</item>
<item>Jupiter</item>
<item>Saturn</item>
<item>Uranus</item>
<item>Neptune</item>
</string-array>
</resources>
public class SpinnerActivity extends Activity implements OnItemSelectedListener { ...
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { // An item was selected. You can retrieve the selected item using
// parent.getItemAtPosition(pos) }
public void onNothingSelected(AdapterView<?> parent) { // Another interface callback
} }
Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
Spinner spinner = (Spinner) findViewById(R.id.spinner);
// Create an ArrayAdapter using the string array and a default spinner layout ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.planets_array, android.R.layout.simple_spinner_item);
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner spinner.setAdapter(adapter);
ViewPager
Vues spécialisées
WebView ScrollView MapView
ViewPager
Pour présenter une galerie en plein écran : pour scroller horizontalement d’un écran à un autre.
1 Déclarer le Viewpager dans un layout.
2 Utiliser un adapter pour remplir le ViewPager Le PageAdapter est le plus courant.
Avec getCount(): retourne le nombres de pages du ViewPager
instantiateItem(View collection, int position): Crée une view pour une position donnée.
destroyItem(View collection, int position, Object view): Supprime une vue d’une position donnée.
3 Agir sur le Viewer depuis le code.
Attacher un listener au viewPager grâce à la méthode setOnPageChangeListener(myPageChangeListener).
Cela permet par exemple d’indiquer le numéro de page sur une TextView.
Souvent lié aux Fragments pour réutiliser des parties d’activité dans d’autres activités (cf. FragmentPagerAdapter).
http://blog.oxiane.com/2011/12/19/utiliser-le-viewpager-pour-slider-dun-ecran-a-un-autre/
CardView
CardView est un élément respectant le style Material Design.
Ajout d’une profondeur dans l’application : 3ème index (z-index en css), l’élévation.
Une image et un texte associé
CardView hérite de FrameLayout
Les informations sont visualisées dans des « Cartes »
Peuvent être personalisées avec des ombres etdes coins arrondis Ombres : via l’attribut card_view:cardElevation.
ATTENTION Simplification en Android 5.0 (API level 21).
Autres attributs utiles :
card_view:cardCornerRadius ouCardView.setRadius.
card_view:cardBackgroundColor.
Exemple
?xmlversion="1.0"encoding="utf-8"?>
<android.support.v7.widget.CardViewxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardBackgroundColor="@android:color/white"
app:cardCornerRadius="2dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
tools:src="@drawable/parisguidetower"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:padding="20dp"
tools:text="Paris"
android:fontFamily="sans-serif"
android:textColor="#333"
android:textSize="18sp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
http://tutos-android-france.com/material-design-recyclerview-et-cardview/
Dialogues
AlertDialog
ProcessDialog
CustomDialog http://developer.android.com/design/building-blocks/dialogs.html
Alert Dialog
// 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// 2. Chain together various setter methods to set the dialog characteristics builder.setMessage(R.string.dialog_message)
.setTitle(R.string.dialog_title);
// 3. Get the AlertDialog from create() AlertDialog dialog = builder.create();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Add the buttons
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) {
// User clicked OK button }
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog }
});
// Set other dialog properties ...
// Create the AlertDialog
AlertDialog dialog = builder.create();
Il y a des boutons : Positifs, négatifs et neutres
RETOUR SUR LES LAYOUT :
QUELQUES SPÉCIFICITÉS
LayoutInflater
Utilisation statique de Layout
setContentView(R.layout.main);
Utilisation dynamique de Layout
Utiliser getSystemService(String) ou getLayoutInflator()
http://ongolearn.blogspot.fr/2014/02/what-is-layoutinflater-why-we-need-to.html
Pour instancier un fichier XML contenant un layout XML dans les vues qui doivent l’utiliser.
Création dynamique de vue (à l’exécution à partir d’un fichier XML).
Exemple
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/an droid"
android:id="@+id/TextView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Add your record"
android:textSize="24sp" >
</TextView>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/layout1"
>
<TextView
android:id="@+id/namelabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your name"
android:textAppearance="?android:attr/textAppearanceLarge" >
</TextView>
<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginTop="14dp"
android:ems="10">
</EditText>
</LinearLayout>
Exemple
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
TextView t = (TextView)inflater.inflate(R.layout.footer,null);
lLayout = (LinearLayout)findViewById(R.id.layout1);
lLayout.addView(t);
Ou
LayoutInflator inflater = getLayoutInflater();
TextView t = (TextView) inflater.inflate(R.layout.footer, null);
lLayout.addView(t);
Fonctionnement de ListView : recyclage et fluidité
http://tutos-android-france.com/listview-afficher-une-liste-delements/
Pour réduire la consommation en mémoire
la ListView stocke seulement les vues qu’elle a la capacité d’afficher,
Lorsqu’une vue sort de l’écran (scroll) elle est réutilisée pour la nouvelle vue à apparaître.
Afin d’éviter d’appeler les méthodes
findViewById à chaque réutilisation des vues, un ViewHolder (gardien/protecteur de vue) : mini contrôleur, associé à chaque cellule, va stocker les références vers les sous vues.
C’est une propriété de la vue (dans l’attribut tag) : une vue n’a qu’un seul ViewHolder, et inversement.
ViewHolder Pattern
Pour éviter les appels frequents à findViewById() pour gérer le scroll d’une ListView (fluidité).
Sans le pattern :
1. La première fois convertView is null. Il faut charger (inflate) et trouver l’item via findViewById().
2. La seconde fois c‘est chargé, il faut retourner chercher l’item findViewById() again.
3. Les fois suivantes il faut toujours chercher l’item avec findViewById() ce qui ralentit la performance si on beaucoup de vues dans la ListView.
https://www.codeofaninja.com/2013/09/android-viewholder-pattern-example.html
ViewHolder Pattern
Avec le pattern :
1. La première fois convertView is null. Il faut charger (inflate), trouver l’item via
findViewById() et créer une instance de ViewHolder correspondante en lui affectant l’id de l’item. Ensuite on affecte le ViewHolder au tag de la convertView.
2. La seconde fois c‘est chargé, on peut accéder à l’item via le ViewHolder associé au tag de la converView qui est non nulle.
3. Les fois suivantes on procède de la même façon ce qui rend le scriolling plus fluide.
Exemple : afficher la liste des Tweets
class TweetViewHolder{
public TextView pseudo;
public TextView text;
public ImageView avatar;
}
View cellule = ...;
TweetViewHolder viewHolder = (TweetViewHolder) cellule.getTag();
if(viewHolder == null){
viewHolder = new TweetViewHolder();
//récupérer nos sous vues
viewHolder.pseudo = (TextView) cellule.findViewById(R.id.pseudo);
viewHolder.text = (TextView) cellule.findViewById(R.id.text);
viewHolder.avatar = (ImageView) cellule.findViewById(R.id.avatar);
//puis on sauvegarde le mini-controlleur dans la vue cellule.setTag(viewHolder);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) { if(convertView == null){
//Nous récupérons notre row_tweet via un LayoutInflater, //qui va charger un layout xml dans un objet View
convertView = LayoutInflater.from(getContext()).inflate(R.layout.row_tweet,parent, false);
}
TweetViewHolder viewHolder = (TweetViewHolder) convertView.getTag();
if(viewHolder == null){
viewHolder = new TweetViewHolder();
viewHolder.pseudo = (TextView) convertView.findViewById(R.id.pseudo);
viewHolder.text = (TextView) convertView.findViewById(R.id.text);
viewHolder.avatar = (ImageView) convertView.findViewById(R.id.avatar);
convertView.setTag(viewHolder);
}
//nous renvoyons notre vue à l'adapter, afin qu'il l'affiche
//et qu'il puisse la mettre à recycler lorsqu'elle sera sortie de l'écran return convertView;
}….
Remplir les cellules avec les données d’un Tweet
Grid View
http://developer.android.com/guide/topics/ui/layout/gridview.html
ACTIVITE
/
Anatomie d’une application
http://developer.android.com/guide/
components/services.html http://designthing.net/the-basic-
components-in-an-android-app
http://developer.android.com/guide/
topics/providers/content- providers.html
http://developer.android.com/guide /components/intents-filters.html
Activité : en quelques mots
Une activité représente un écran avec son interface utilisateur.
Par exemple dans Polynews, vous avez l’activité principale et une activité correspondant à chaque vue spécifique associée à un type d’actualités.
Bien que l’ensemble des activités réponde à un besoin commun qui est celui de l’application visée, chaque activité est indépendante.
Dans tous les cas il y a une activité de démarrage (classe main / run java) susceptible d’utiliser d’autres ativités.
Par exemple, vous pouvez faire démarrer une app de camera pour prendre une photo et contribuer à une actualité.
Toute activité est une sous classe d’Activity Qui hérite de l'interface Context pour
représenter tous les composants d'une application. Dabs le package
android.app.Activity..
https://openclassrooms.com/courses/creez-des-applications-pour-android/votre-premiere-application-1
Arbre d’héritage d’activités
android.app.Activity
AppCompatActivity TabActivity LauncherActivity, PreferenceActivity, AccountAuthenticatorActivity ActivityGroup AliasActivity ListActivity
FragmentActivity
NativeActivity
ActionBarActivity
ExpandableListActivity
Cycle de vie d’une activité
http://supertos.free.fr/supertos.php?page=1076 Cycle de vie global
onCreate() -> onDestroy()
Cycle de vie visible onStart() -> onStop()
Affichée à l’écran mais peut ne pas être utilisable
(en second plan)
• Cycle de vie en premier plan
• onResume() -> onPause()
Les activités dans le Manifest
A déclarer absolument dans le fichier Manifest
1 <?xml version="1.0" encoding="utf-8"?>
2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="com.example.android"
4 android:versionCode="1"
5 android:versionName="1.0">
6
7 <uses-sdk android:minSdkVersion="10" />
8
9 <application android:icon="@drawable/icon" android:label="@string/app_name">
10
11 <activity android:name=".MyActivity" android:label="@string/app_name">
12
13 <intent-filter>
14 <action android:name="android.intent.action.MAIN" />
15 <category android:name="android.intent.category.LAUNCHER" />
16 </intent-filter>
17
18 </activity>
19
20 <activity android:name=".MySecondActivity" android:label="@string/app_name" />
21
22 </application>
23 </manifest>
Implémentation d’une activité
Implementer les méthodes correspondant aux transitions entre différents états du cycle de vie.
Les deux plus importantes sont : onCreate()
Appelée à la création de l’activité : intialisert les principaux composants. C’est là qu’on appelle setContentView() pour définir le layout de l’interface utilisateur de cette activité.
onPause()
appelée lorsque l’utilisateur quitte l’activité ce qui n’est pas toujours équivalent à détruire l’activité. Il faut penser à sauvegarder ce qui doit être persistant pour la prochaine session.
Implémenter les méthodes du cycle de vie permet de fournir une experience utilisateur fluide.
Resumed
L’activité a le focus utilisateur.
Paused
Une autre activité a le focus, mais celle-ci est toujours visible. L’activité est en
mémoire et reste attachée au gestionnaire de fenêtre mais elle peut être tuée par le système si il devient trop lent.
Stopped
L’activité est en background. L’activité est en mémoire n’est pas attachée au gestionnaire de fenêtre. Elle n’est pas visible à l’utilisateur et peut être tuée par le système.
Evénements et lien entre activités
Gestion d’une Pile d’activités
Lorsqu’une activité en démarre une autre leur cycle de transitions sont liés. La
première activités passe à paused ou à stopped. ATTENTION aux données persistantes partagées. Il y a un overlap entre la création de la seconde et laterminaison de la
première.
Si A démarre B alors
La méthode onPause() de A est exécutée
Les méthodes onCreate(), onStart(), et onResume() de B sont ensuite exécutées en séquence.
B a le focus.
Si A reste longtemps non visible à l’écran alors sa méthode onStop() est appelée Ecrire dans la base de données pendant onPause au lieu de onStop
Gestion de la cohérence entre activités
Lorsque l’activité est détruite pour récupérer de la mémoire l’instance Activity est détruite et recréée si besoin.
Pour que l’état de l’activité soit préservée il faut implémenter la méthode onSaveInstanceState().
Cette méthode est appelée par le système vant une susceptible destruction.
Paramètre : un Bundle dans lequel sauver l’état avec des paires nom/valeur.
Le Bundle est ensuite passé en paramètre à onCreate() et onRestoreInstanceState().
Passage de données
ATTENTION aux changements de configuration des devices à l’exécution
(orientation, keyboard, language).
Ils impliquent un onDestroy(), suivi de onCreate().
On peut fixer une orientation et interdire les autres :
< activity
android :name= ".MainActivity"
...
android :screenOrientation= "portrait"
android :configChanges= "orientation">
android:screenOrientation indique que l’activité doit rester bloquée en mode portrait.
android:configChanges empêche les fonctions onResume() et onPause() de l’activité d’être appelées lorsque l’orientation de l’appareil change. Sans cette ligne, la vue ne changera pas si l’utilsateur tourne son téléphone, mais ces méthodes seraient malgré tout appelées .
Activités et Orientation
MainActivity
Structure du layout et lien avec le code
Deux fichiers principaux ont été créés : un fichier Java contenant le code Java de l’activité et un fichier layout/activity_main.xml qui contient la structure de composant graphiques de l’activité.
Structure du fichier layout/activity_main.xml
L’activité et ses composants sont initialisés dans la méthode onCreate() de MainActivity.
A travers les attributs tels que android:id. Cet attribut permet de définir un identifiant unique pour les éléments graphiques d’un layout. Le + signifie que c’est ici que l’élément est défini.
FRAGMENT
En Android, les fragments sont des portions d’interface graphiques. On peut combiner
plusieurs fragments dans une activité et réutiliser les fragments dans différentes activités. Ils permettent donc un meilleur découpage ainsi qu’une meilleure évolutivité du code. La
combinaison de fragments permet par exemple de créer des vues différentes selon le type d’appareil (tablette ou téléphone), sans avoir à dupliquer du code.
Un fragment pourquoi ?
Un fragment a :
Son propre cycle de vie,
ses propres événements en entrée
on peut rajouter ou retirer un fragment d’une activité en cours d’exécution
Le cycle de vie d’un fragment est directement impacté par le cycle de vie de l’activité dans laquelle il se trouve,
Si activité est paused/destroyed alors tous les fragments sont paused/destroyed
Pendant qu’une activité est utilisée on peut manipuler chaque fragment indépendament (les ajouter ou les supprimer)