Démarrer avec le développement multiplateforme IOS Android de A à Z


Télécharger Démarrer avec le développement multiplateforme IOS Android de A à Z

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :



 
 

¨  2008 : G1 => Mobiles, tablettes, TV…

¨  Pile logicielle Open-Source supportée par l’Open

Handset Alliance

¨  Système d’exploitation open source

3

¨  Applications natives : browser, mail, contacts…

¨  Environnement de développement : Android Studio

¨  Développement de surcouches par les constructeurs (ex

HTC), opérateurs…

¨  Développement gratuit

Le marché il y a 10-6 ans

5

Le marché ces 6 dernières années

APIs : accès complet au matériel

¨  Réseau GSM, EDGE, 3G, 4G, LTE…

¨  Géolocalisation :Google Map, géocodage

¨  Communication : Wifi, bluetooth, NFC

6

¨  Capteurs : alimentation, champ magnétique

¨  IHM : Multimédia, navigateur HTML5, Widgets, fonds d ’écran

¨  Graphisme : openGL ES 2.0, 2D, 3D

¨  Services arrière plan : tâches de fond, notifications

¨  Data : Bases de données tte, fournisseurs de contenu ¨ Google Maps

¨  Services d’arrières plan

7

¨  Données partagées et communication interprocessus

¨  Applications natives = applications tierces

¨  Android Beam

¨  Widget, WallPaper, boite de recherche

8


 

Machines virtuelle Dalvik -> ART

9

¨    Dalvik compile à la volée les applications, lors de leur lancement, alors que ART compile les applications lors de leur installation.

¨    La durée d’installation sera donc plus longue mais les avantages sur le long terme ne sont pas négligeables.

 
 

¨    Lancer Android Studio

¨    New->New Project

¨    Choisir Empty Activity [Default]

¨    Application Name : Hello

¨    Company Domain:

11

¨    Phone & Tablet [x]

¨    Min SDK: API 19

¤

¨    Empty Activity

¨    Activity Name : HelloDolly

¨    Title : Hello Dolly

12

Execution : AVD ou Mobile     

13


 

14


 

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

15

setContentView(R.layout.activity_main); // affichage vue

}

}

<?xml version="1.0" encoding="utf-

8"?><android.support.constraint.ConstraintLayout

xmlns:android=";

xmlns:app="; <RelativeLayout … > : layoutManage xmlns:tools="; android:layout_width="match_parent"

android:layout_height="match_parent"     "match_parent" / "wrap_content" tools:context=".MainActivity">   

16

            <TextView                                 padding : espacement

android:layout_width="wrap_content" android:layout_height="wrap_content"     @dimen: android:text="Hello World!"       

app:layout_constraintBottom_toBottomOf="parent" ".MainActivity" ßIHM (Themes…) app:layout_constraintLeft_toLeftOf="parent"

appapp::layout_constraintRight_toRightOflayout_constraintTop_toTopOf="parent" ="parent"        <TextView … > : display text

/></android.support.constraint.ConstraintLayout>

"@string/hello_world"

<resources>

17

<string name="app_name">Hello</string>

<string name="hello_world">Hello world!</string>

</resources>


 

19


 

20

   
 

22

   

Ajout d’un champ de saisie et d’un bouton qui change le texte

-> nouvelles notions qui vont être apprises

¨  Modif de res/layout/

¨  Ajout <EditText …/>, <Button …>

23

¨  Les paramètres obligatoires

¨  Le placement dans un ConstraintLayout

¨  Les autres gestionnaires

¨  L’id d’un élément

¨  La gestion des events

24

   

25

Les Layout pour le placement

 

ConstraintLayout : référence depuis 2016 Notion de poids (LinearLayout), chargement rapide

Placements des éléments

¨  Alignement avec le parent

¨  Alignement entre élèments (coté/bords)

26

¨  Alignement de la BaseLine : text

¨  Ajuster le Bias : % (weight)

¨  Les Chains : (selection puis click droit)

¤ alignement sur une ligne

¤ Sélection 2 élts, choix du type de la chaine (icone)

¤ choix du weight (avec 0dp comme width)

Placements des éléments

27

¨  Rajouter une guideline

¤ Permet de faire des lignes de positionnement pour plusieurs éléments par exemple

¨  Rajouter une barrière

android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" />

28

<EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/textView1" android:inputType="text" />

<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_below="@id/editText1" android:text="@string/ok" />

¨  Obligatoires : placement

¤ android:layout_width="match_parent"

¤ android:layout_height="wrap_content"

29

¤ android:inputType="text" (voir graphical layout)

¨  Identifiant

¤ Définition : android:id="@+id/editText1"

¤ Ex. utilisation : android:layout_below="@id/editText1"

1ère solution : gestion inline de l’event Button dans le .java

¨ Implémentation inline du onClickListener

 

30

public boolean onCreate(Bundle savedInstanceState) {

 

}

2nde solution : implémentation du listener du Button dans le .java

            ¨   Implémentation onClickListener par la classe

public class MainActivity extends Activity implements OnClickListener{

… onCreate {

Button but1 = (Button)findViewById(R.id.button1); but1.setOnClickListener(this);

31

}

/** Manage buttons */ @Override public void onClick(View v) { switch (v.getId()){ case R.id.button1 :

((TextView)findViewById(R.id.textView1)).setText("You said: "

+((EditText)findViewById(R.id.editText1)).getText());

break ; default :

Log.d(TAGS,"Event not managed");

}

}

3ième solution : Gestion de l’event Button dans le xml

32

¨  Le plus simple pour les bouttons, ajouter dans android:onClick="sendMessage"

¨  Dans

/** appelé quand on clique*/ public void sendMessage(View view) {

// blabla …

}

Exercice

¨   Créer une fenêtre de connexion avec passwd et date de naissance

33

¨   Le passwd est caché et c’est lui qui déclenche l’action d’affichage d’un Message : « Welcome » si le passwd est « xxx », sinon il affiche « invalid passwd »

¤  Toast.makeText(getApplicationContext(),

"youhou ",Toast.LENGTH_SHORT).show();

¤  Attention, il faut utiliser getText().toString() pour transformer en String

¨   Un button OK est ajouté, il permet également de déclencher la connexion

¨   Un button Erase pour effacer le  2 champs

¨   Mettre des valeurs indicatives (PlaceHolder) dans les EditText : Hint


 

Exemple : lancer une autre Activity

36

MainActivity -> Lancement de l’activité DetailActivity

Intent intent = new Intent(, DetailActivity.class); startActivity(intent); // démarrage activité

Exemple : lancer une autre Activity

Passage d’une donnée (« Hello ») à l’Activity

37

// définition d’un nom (identifiant)  pour la donnée public final static String EXTRA_MSG = "isfen.hello.MESSAGE ";

Intent intent = new Intent(, DetailActivity.class);

// ajout de la valeur du paramètre NOM intent.putExtra(EXTRA_MSG, "Hello"); startActivity(intent); // démarrage activité

38

Création de DetailActivity

File->New->Activity->Empty Activity

 

¨ <?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="; package=".helloisen2intent" >

<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >

39

<activity android:name=".MainActivity" android:label="@string/app_name" >

<intent-filter>

<action android:name="" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name=".DetailActivity"

android:label="@string/title_activity_detail" android:parentActivityName=".MainActivity" >

<meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".helloisen2intent.MainActivity" />

</activity>

</application> </manifest>

Selection de l’application de lancement

<activity android:name=".MainActivity" android:label="@string/app_name">

40

<intent-filter>

<action android:name="" />

<category android:name="android.intent.category.LAUNCHER »/>

</intent-filter>

</activity>

Get an Intent : dans activité lancée

41

¨ Récupération de l’intention qui a déclenché la création d’une application

protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState);

Intent it = getIntent() ; String str= it.getStringExtra(MainActivity.EXTRA_MSG);

42

Cycle de vie d’une activité

(Optionnel)  Gestion du destroy de l’application

@Override

43

public void onDestroy() { super.onDestroy(); // Always call the superclass

// Stop method tracing that activity started during onCreate() .Debug.stopMethodTracing();

}

Que faire pendant le onPause()

44

¨  onPause : quand l’appli est encore visible par transparence, mais derrière une autre appli.

¨  Stoper les animations qui peuvent consommer.

¨  Sauvegarder les contenus. Ex : draft Mail

¨  Libérer les ressources system (Gps, écouteurs d’évènements..) pour limiter l’utilisation de la batterie

Stopping and Restarting an Activity

45

¨  Switch d’une application à une autre

¨  Lancement d’une autre activité à partir de l’activité

(ex : lancement du browser, du mail…)

¨  Réception d’un appel téléphonique

TP

¨  Faire une fenêtre avec une demande de mot de passe et son nom

¨  Rajouter un bouton qui permet d’envoyer un mail à l’administrateur quand on a perdu son mot de passe

46

¨  Afficher bonjour ‘le nom’ dans la seconde fenêtre

¨  Faire une belle interface avec une image de fond et responsive

¨  Aide technique

¤  Créer une classe contenant les constantes, EXTRA_MSG…, faire son import static (java 1.5) dans les autres classes

import static .hello_intent.Constantes.* ;

¤  Attention, il faut utiliser text.getText().toString() pour transformer en String pour l’Intent

Début du projet

47

¨  Les promenades sonores, radio Grenouille

¨ 

¨  Julie de Muer

 

UX : Navigation Intuitive

48

¤ Les 3 leviers n Regroupement logiques d’informations n Animations pour l’information et le guidage n Focus sur l’important

 

49

UX : Analyse de la navigation

¤ Définir la structure de navigation

¤ Définir la hierarchie

50

[structure de navigation] Définition de la structure

1.      Faire l’inventaire des utilisateurs et des tâches

2.      Pour chaque utilisateur, définir la priorité des tâches

3.      Définir le séquencement des tâches

 

[structure de navigation] [Définition]

51

1) Inventaire

¤ Des utilisateurs

¤ Des tâches

¤ Exemple : App restaurant

 

[structure de navigation] [Définition]

52

2) Priorité

¤ En plusieurs catégories, ou priorité décroissante

¤ A faire pour chaque utilisateur si nécessaire

 

[structure de navigation] [Définition]

53

3) Définition des séquencements

¤ A faire pour chaque utilisateur si nécessaire

¤ Séquencement se recoupent parfois

 

54

[structure de navigation]

Définition de Hierarchie

 

Exercice UX

¤ Définir les utilisateurs

55

¤ Définir les actions

¤ Proposer des exemples de n Regroupement logiques d’informations n Informer, guider l’utilisateur par des Animations n Mise en valeur du contenu important

UX :

56

¤ Proposer des exemples de n Regroupement logiques d’informations n Informer, guider l’utilisateur par des Animations n Mise en valeur du contenu important

57

Navigation Drawer

¤ Un Menu à gauche de Navigation

 

Début projet avec NavigationDrawer

58

¨ Créez un nouveau projet

¤ Prendre à partir de la version Kit-Kat

¤ Créer Navigation Activity

¤ Title : Ballades Sonores

59

Fichiers générés

¨ DrawerLayout :

¤ CoordinatorLayout :

 

Contenu de la frame principale

¨ CoordinatorLayout (extends FrameLayout)

60

Création d’interactions entre les Childs : drags, swipes, flings,

¤ AppBarLayout : barre du haut

¤ + contenu principal : (Hello World)

¤ FloatingActionButton: button à supprimer si non nécessaire

61

NavigationView () header + menu

Customisation (1/2) => changement couleur (…img)

¨ Changement des couleurs

62

¤ De l’icône, du texte

¤ De l’image de fond des items // img on verra plus tard

<android.support.design.widget.NavigationView

app:itemIconTint="#2196f3" app:itemTextColor="#00DD00" app:itemBackground="@drawable/my_img"

Customisation (2/2) =>Chgt couleur selected/not

¨  Changer de couleur en fonction de l’état

¨  [Studio] Créer un répertoire color dans res (new/android resource dir)

63

¤    Créer un fichier: (new/android resource file)

<selector xmlns:android=";>

<!-- This is used when the Navigation Item is checked -->

<item android:color="#ee6911" android:state_checked="true"/>

<!-- This is the default text color -->

<item android:color="#1199EE"/>

</selector>

¤    Remplacer l’affectation de la couleur par:

app:itemTextColor="@color/state_list"

On peut faire pareil pour l’icone et le background

Ajoutons du contenu dans l’Activity

64

¤ Il faut ajouter des Containers pour ajouter plusieurs page internes (Fragment)

¤ Dans les containers, il faut ajouter des Fragments

¤ Exemple de fragment : Page Vide, liste, Map…

¤ Ajoutons du swipe pour changer de fragment

65

Layout: une page qui défile avec indicateur : ViewPager+PagerStrip

¤ du swipe entre les pages : ViewPager

¤ Un indicateur des tabs : PagerStrip

¤ Des Fragments pour chaque fenêtre interne

 

1/5 Modification res/layout/

¨ ViewPager+ PagerTabStrip : remplacer entièrement le code de par

<?xml version="1.0" encoding="utf-8"?>

66

<.ViewPager xmlns:android="; xmlns:app="; android:id="@+id/pager"

app:layout_behavior="@string/appbar_scrolling_view_behavior"

android:layout_width="match_parent" android:layout_height="match_parent">

<.PagerTabStrip android:id="@+id/pager_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:backgroundTint="#002299" android:textColor="#f00"/>

</.ViewPager>

2/5 Création des fragments internes

Exemple de création d’un fragment avec écrit

67

« Home »

¨ File->New->Fragment ->Fragment Blank

¨ Exemple HomeFragment

¨ Décocher “include interface callback »

 

68

3/5 Modification

Modif du .xml du fragment

¨   Changer la couleur

¨   changer le texte


4/5 MainActivity incluant PagerAdapter (1/2)

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, HomeFragment.OnFragmentInteractionListener {

Fragment[] allFrags = new Fragment [1] ; // création d’un tableau contenant les fragmets

ViewPager mViewPager; // gestionnaire de pages (Pager)

FragsPagerAdapter fragsPagerAdapter; // adapteur des fragments au Pager : classe interne

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); allFrags[0] = HomeFragment.newInstance(); // création de la fenetre interne : fragment setContentView(R.layout.activity_main);

// Création de l’Adapter des fragments et on l’affecte au viewPager

// ViewPager use support library fragments, so use getSupportFragmentManager.

fragsPagerAdapter = new FragsPagerAdapter(getSupportFragmentManager()); mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(fragsPagerAdapter);

}

Attention, pour les import il faut utiliser PARTOUT  : import .Fragment ;

ATTENTION Modifier pour qu’il n’y ait plus de passage de paramètre

4/5 MainActivity incluant PagerAdapter (2/2)

@Override

public void onFragmentInteraction(Uri uri) {

}

/**

* adapteur minimaliste pour les fragments

70

*/ public class FragsPagerAdapter extends FragmentStatePagerAdapter { public FragsPagerAdapter(FragmentManager fm) { super(fm);

} @Override

public Fragment getItem(int i) { return allFrags[i];

}

@Override

public int getCount() { return allFrags.length ;

}

@Override

public CharSequence getPageTitle(int position) { return "Title " + (position+1);

}

}

}

4/5 [UX] Gestion de la touche Back

La touche back doit permettre de revenir aux fragments précédents, puis à quitter l’application si on est dans le fragment tout à gauche

Modifier la méthode onBackPressed pour qu‘elle corresponde au code suivant

@Override

public void onBackPressed() {

DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START);

} else { if (mViewPager.getCurrentItem() == 0) {

// If the user is currently looking at the first step, allow the system to handle the // Back button. This calls finish() on this activity and pops the back stack.

super.onBackPressed();

} else {

// Otherwise, select the previous step.

mViewPager.setCurrentItem(mViewPager.getCurrentItem() - 1);

}

}

Remarque : 2 types d’Adapter existent

¤ FragmentPagerAdapter

72

Nombre fixe et faible de pages

¤ FragmentStatePagerAdapter

Nombre de pages indéterminé, construction, destruction à chaque mouvement de doigt, minimize l’utilisation mémoire


Création d’autres fragments

¨ Créer 2 fragments en utilisant les facilités Visual Studio

New->fragment->fragment(List)

New->fragment->fragment (Blank)

MapFragment :

supprimer la méthode onButtonPressed

Modification des interfaces

¨   Les deux classes de Fragment générées incluent automatiquement les interfaces :

MapFragment.OnFragmentInteractionListener

ListBalladesFragment.OnListFragmentInteractionListener

Ces interfaces servent à définir la communication entre les Fragment et la MainActivity

¨   Il faut implémenter ces interfaces dans MainActivity

Class MainActivity … implements MapFragment.OnFragmentInteractionListener, ListBalladesFragment.OnListFragmentInteractionListener

¨   Les modifier si nécessaire pour faire passer les paramêtres souhaités. Par exemple faire passer une String au lieu de l’URI par défaut dans MapFragment.OnFragmentInteractionListener

Explication sur le Wizard Fragment (list)

¨ Le Wizard Fragment (list) crée

¤  : layout affichant une list ¤ : layout d’un elt de la list

¤  BalladeFragment : le Fragment qui fait correspondre une vue à une List de données

¤  MyBalladeRecyclerViewAdapter : l’Adapter qui crée une vue adaptée aux données

¤  DummyContent: une classe créant des Données fictives (Dummy) n DummyItem: une donnée fictive

Pour son propre projet il faut remplacer Dummy et Adapter


TP

¨  Créer l’appli avec les fragments : Home, List,

Apropos

76

¨  Modifier la liste pour y ajouter du contenu récupéré à partir du site web des promenades sonores

"12", "Le Fantôme du Sémaphore"

"4", "Gardanne 3 couleurs"

"19", "Frioul"

animation des transitions

¨  Gestion affichage des pages et adjacentes

¨  Faire une classe (ex ZoomOutPageTransformer) qui implémente ViewPager.PageTransformer

77

¤ public void transformPage(View view, float position) ; ¤ position [-1, 1] => 0 quand centrée

¤ utiliser setAlpha(), setTranslationX(), setScaleY(), setRotation() .

¨ Affectation du Transformer au ViewPager

mViewPager.setPageTransformer(true, new ZoomOutPageTransformer());

Exemple 1 : Zoom

public class ZoomOutPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.85f; private static final float MIN_ALPHA = 0.5f;

public void transformPage(View view, float position) { int pageWidth = view.getWidth(); int pageHeight = view.getHeight();

if (position < -1) { // [-Infinity,-1) This page is way off-screen to the left. view.setAlpha(0);

} else if (position <= 1) { // [-1,1]

78

// Modify the default slide transition to shrink the page as well float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); float vertMargin = pageHeight * (1 - scaleFactor) / 2; float horzMargin = pageWidth * (1 - scaleFactor) / 2; if (position < 0) { view.setTranslationX(horzMargin - vertMargin / 2);

} else { view.setTranslationX(-horzMargin + vertMargin / 2);

}

// Scale the page down (between MIN_SCALE and 1) view.setScaleX(scaleFactor); view.setScaleY(scaleFactor);

// Fade the page relative to its size. view.setAlpha(MIN_ALPHA +

(scaleFactor - MIN_SCALE) /

(1 - MIN_SCALE) * (1 - MIN_ALPHA));

} else { // (1,+Infinity]

// This page is way off-screen to the right. view.setAlpha(0);

}

}

}

Exemple 2 : Venir du fond

¨        public class DepthPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.5f;

public void transformPage(View view, float position) { int pageWidth = view.getWidth();

if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0);

} else if (position <= 0) { // [-1,0]

79

// Use the default slide transition when moving to the left page view.setAlpha(1); view.setTranslationX(0); view.setScaleX(1); view.setScaleY(1);

} else if (position <= 1) { // (0,1] // Fade the page out. view.setAlpha(1 - position);

// Counteract the default slide transition view.setTranslationX(pageWidth * -position);

// Scale the page down (between MIN_SCALE and 1) float scaleFactor = MIN_SCALE

+ (1 - MIN_SCALE) * (1 - Math.abs(position)); view.setScaleX(scaleFactor); view.setScaleY(scaleFactor);

} else { // (1,+Infinity]

// This page is way off-screen to the right. view.setAlpha(0);

}

}

}



 

Ajout d’items dans la barre

<menu xmlns:android="; xmlns:app="; xmlns:tools=";

81

tools:context=".MainActivity">

<item android:id="@+id/action_search" android:icon="@android:drawable/ic_search" android:title="@string/action_search" app:showAsAction="ifRoom"

/>

<item android:id="@+id/action_settings" android:title="@string/action_settings"

android:orderInCategory="100" app:showAsAction="never" />

</menu>


 

TP préliminaire

On veut déclencher une activité Ballade quand on clique sur un élément de la liste des ballades

83

¨  Créer l’activity BalladeDesc contenant 2 tabs

¤ File/New/Activity/Tabbed Activity

¤ Dans [Navigation Style], selectionner Action Bar Tabs (with ViewPager)

¨  Info : le déclenchement de l’Intent pour créer la nouvelle activité se fait dans onFragmentInteraction()


 

¨  RelativeLayout : les uns par rapports aux autres et aux bords de l’écran

¨  LinearLayout : horizontal ou vertical avec des notions de weight (proportion)

85

¨  FrameLayout : supperposition en haut à gauche ¨ GridLayout (API 14) : grille

¨ 

Création d’un linear Layout

<RelativeLayout

… composants

86

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" >

… rajouter les composants en leur donnant des poids

</LinearLayout>

</RelativeLayout>

LinearLayout dimensionnement

¨  Définir proportions et les autres complètent la place

¨  Exemple

Premier composant

87

¤  android:layout_weight= "1"

¤  android:layout_width="0dp »

Second composant

¤  android:layout_weight= "2"

¤  android:layout_width="0dp »

Le dernier composant meublera

88

A partir de données et d’un Adapter

String [] myTab = {"un","deux", "quatre"};

ArrayAdapter <String> adapter= new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, myTab); ListView listView = (ListView) findViewById(R.id.listview); // id : listView.setAdapter(adapter);

Création Adapter Custom pour une ListView (1/3)

Pour un Fragment : File->New->Fragment->Fragment List 

2)   Création du layout de l’item (l) : (2/3)

89

3)   Création de l’Adapter

¤  utiliser un SimpleAdapter (3/3)

¤  OU Création d’une classe BalladeItemAdapter qui hérite de BaseAdapter

¤  OU modifier l’adapter automatiquement généré par Android Studio (MyItemReViewAdapter)

4)   Modification Java (ListBalladeFragment) généré : tp. 3/3

¤  Création d’une ArrayList de HashMap pour les Data

¤  Affection de l’Adapter

Modification du .xml généré pour la liste

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="; xmlns:tools=";

90

android:layout_width="match_parent" android:layout_height="match_parent"

tools:context="fr.grenouille.balladessonores_db.ListBalladesFragment">

<ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" />

</FrameLayout>

Création du .xml (Layout) pour l’item (2/3)

               ¨   <?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="; android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"

91

<ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:padding="10px"

/>

<LinearLayout xmlns:android="; android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="10px" android:layout_weight="1"

<TextView android:id="@+id/titre" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textSize="16px" android:textStyle="bold"

/>

<TextView android:id="@+id/description" android:layout_width="fill_parent" android:layout_height="fill_parent"

/>

</LinearLayout>

</LinearLayout>

Modification Java généré: (2/3)

// création de données pipo pour l’image, le nom et l’auteur

List listItem ;

@Override

92

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);

}

// creation des données

listItem = new ArrayList<HashMap<String, String>>(); // recup d’un cursor avec le descritpif de la ballade

for(int i=0 ; i < 3 ; i++) {

HashMap<String, String> map = new HashMap<String, String>();      // img id

String imgId = String.valueOf(getResources().getIdentifier("promenade_icon_"+i, "drawable", getActivity().getPackageName())); Log.d(TAGS, "ImgId : "+imgId) ; ("imgId", imgId); ("Nom", "Nom_"+i); ("Auteur", "Auteur_"+i);

listItem.add(map);

}

}

Modification Java généré: (2/3)

// Création et affectation de l’adapter

// ajout du listener de click

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_item_list, container, false);

AbsListView mListView = (AbsListView) view.findViewById(.list);

// Set the adapter

93

SimpleAdapter mAdapter = new SimpleAdapter(getActivity(), listItem,

R.layout.item_listballade, new String[] {"imgId", "Nom","Auteur"},

new int[] {R.id.img, R.id.titre, R.id.description}); ((AdapterView<ListAdapter>)mListView).setAdapter(mAdapter);

mListView.setOnItemClickListener(new AbsListView.OnItemClickListener() {

@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (null != mListener) mListener.onListFragmentInteraction(); }

});

return view;

}

Création des images

94

Utiliser l’outil Android file resizer pour générer les images des icones avec comme nom :

Ou le plugins :

Rajouter le listener sur une Liste

¨  1) Rajouter à ListBalladeFragment implements AbsListView.OnItemClickListener

95

¨  2) Rajouter la méthode qui appelle le listener public void onItemClick(AdapterView<?> parent,



View view, int position, long id) {

mListener.onFragmentInteraction(position);

}


 

Les ressources String ou autre fichier

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="prenom">"Bob"</string>   

<string name= "nom">"<b>Lafleche</b>"</string>   

<string name="directeur">Directeur : %s</string>

97

<!-- Promo number (year) -->

<integer-array name="numPromo">

<item>2013</item>

<item>2014</item>

<item>2015</item>

</integer-array>

<!-- goldfather name -->

<string-array name = "parrainsPromo">

<item>Onet</item>

<item>Al Pacino</item>

<item>Fantomas</item>

</string-array>

</resources>

¨  Depuis un autre .xml : @type/nom_var

@string/directeur

@color/transp_blue

@drawable/ic_launcher

¨  Depuis un code .java, les méthodes nécessitent un identifiant ou une instance ¤ Pour récupérer l’ identifiant d’une ressource : R.type.nom_var

98

R.string.directeur

R.color.transp_blue

R.drawable.ic_launcher

¤ Pour récupérer la valeur de la ressource : getRessources().getType(identifiants)

String name = getResources().getString(R.string.directeur); int coul = getResources().getColor(R.color.transp_blue);

Exemples de ressources

// method which requires identifiant

Toast.makeText(getApplicationContext(), ,Toast.LENGTH_SHORT).show();

// get the name of the boss in the ressources : instance

99

String directeur = String.format(getString(R.string.directeur), getString(R.string.nom));

// get all the name of the boss and concat

String[] dir=getResources().getStringArray(R.array.parrainsPromo) ;

String allDir = "" ; for(String name : dir) allDir = allDir.concat(name+"\t");

Les ressources (ou autre .xml)

100

<resources>

<color name="deep_blue">#00F</color>

<color name="transp_blue">#AA0000FF</color>

</resources>

Formats : #RGB, #TRGB, #RRGGBB, #TTRRGGBB

 

Les ressources (ou autre .xml)

<resources>

<!-- Default screen margins, per the Android Design guidelines. -->

<dimen name="activity_horizontal_margin">16dp</dimen>

101

<dimen name="activity_vertical_margin">16dp</dimen> </resources>

Formats : px (pixel),in, pt, mm, dp (pixel density), sp (dp pour Font)

Utilisation dans le layout ()

android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"

102

Les ressources, définition dans et utilisation dans les

style:ensemble de propriétés pour une View (Font,size,color,

L’utilisation des styles d’android

103

Mieux que de redéfinir des styles, utiliser le thème courant avec ?android:

<!–- . -->

<TextView

… android:textColor="?android:textColorPrimaryInverse"

/>


L’utilisation des thèmes

Theme.Light, Theme.Translucent, Theme.NoTitleBar.Fullscreen, Theme.Black…

Ex. création "CustomTheme" à partir Theme existant :Theme.Light

<!–- à

<color name="custom_theme_color">#b0b0ff</color>

<style name="CustomTheme" parent= "Theme.AppCompat.Light">

<item name="android:windowBackground">@color/custom_theme_color</item>

104

<item name="android:colorBackground">@color/custom_theme_color</item>

</style>

Définition thème pour l’application ou chaque activité séparément

 

For more about creating and using themes, read the Styles and Themes guide.

Les autres ressources

¨   Les images : mimmap/ pour les icones et drawable/ pour les autres

105

¨   les layout : positionnement des élts dans les fenêtres ¨ les menus

¨   les animations…

¨   Utiliser les ressources système : rajouter le mot android

¤  (.xml)         android:textColor="@android:color/dark_gray"

¤  (.java)        …getResources().getColor(android.R.color/dark_gray);

Selection des ressources

¨  

¨   Mobile Country-Code : mcc234-mnc20/mcc310/…

¨   Langue et région : en/fr/en-rUS/en-rGB/…

¨   Largeur minimale de l’écran : sw320dp/sw600dp/…

¨   Largeur de l’écran disponible : w320dp/w600dp…

¨   Hauteur d’écran : h480dp/…

¨   Taille de l’écran : small/medium/karge/xlarge, …

106

¨   Largeur/Longueur

¨   Orientation : port/land/square

¨   Mode dock, car ou desk

¨   Mode nuit ou jour : night/notnight

¨   Densité de l’écran en pixel : ldpi, mdpi, hdpi, xhdpi,…

¨   Type d’écran tactile : notouch/stylus/finger

¨   Visibilité du clavier : keysexposed/keyshiddden/keyssoft

¨   Type du clavier : nokeys/qwerty/12key

¨   Visibilité touche de navigation : navexposed/navhidden ¨  Type de navigation : nonav/dpaad/tracball/whelle ¨   Version de l’api : v7/v8….

Exemple : layout-xlarge-port-keyshidden, layout-large-land, …


Exemple de la langue

¨ 

MyProject/ res/

107

values/

values-es/

values-fr/ .

¨  Le fichier sera automatiquement chargé en fonction de la valeur du paramètre ‘langue’ du mobile

Plusieurs taille d’écrans : gestion des layouts

¨  4 tailles : small, normal, large, xlarge…

108

¨  4 densités de pixels : low (ldpi), medium (mdpi), high (hdpi), extra high (xhdpi)…

-> Créer une arborescence

MyProject/ res/ layout/

layout-large/

Plusieurs layout en fonction de l’orientations et taille d’écrans

109

MyProject/ res/ layout/ # default (portrait)

layout-land/         # landscape

layout-large/        # large (portrait)

layout-large-land/   # large landscape

110

Rapport de tailles des images entreelles

¨  mdpi: 1.0 (baseline) : 48x48 pour icônes

 

¨ 

Click gauche sur res/ new Image Asset

-> Modification de

Gestion des tailles des images

MyProject/ res/ drawable-xxxxxxxhdpi/

112

drawable-xxhdpi/

drawable-xhdpi/

drawable-hdpi/

drawable-mdpi/

drawable-ldpi/

Changement dynamique de ressources

¨   On remplacer la lecture d’un fichier de ressource sur un événement en surchargeant dans l’Activity

public void onConfigurationChanged(Configuration newConfig) { }

Déclencheurs : mcc, mnc (évt carte SIM), locale,

113

keyboardHidden,keyboard,fontScale, uiMode (voiture/jour/nuit),orientation, screenLayout(activation nouvel écran), screenSize

(portrait/paysage),smallestScreenSize

¨   rajouter les déclencheurs séparés par ‘|’dans <activity ….> (Manifest) android:configChanges="screenSize|orientation|keyboardHidden »

¨   rajouter dans l’activité ()

public void onConfigurationChanged(Configuration newConfig) {

super.onConfigurationChanged(newConfig);

if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE) …

TP

¨  Créer l’activity Ballade contenant 2 tabs

¤  Ecouter

¤  Info

114

¨  Customisez la liste pour qu’elle contienne des images et du texte

¨  A partir des données récupérées sur le site pour 2 ballades ¨ Afficher les informations dans l’un des Fragments

¤  Titre

¤  Numéro

¤  Durée

¤  …


 
 
 
 
 

Recopie en local dans Asset

¨  Créer le répertoire "assets"

120

File->New->Folder->Assets Folder

¨  Recopier .json dans le répertoire assets

¨  Lecture d’un fichier placé dans "assets"

BufferedReader br = new BufferedReader(new InputStreamReader(context.getAssets().open("")));

Transformation du fichier Json en String

// build the string from json

try {

BufferedReader br = new BufferedReader(new       

InputStreamReader(getContext().getAssets().open("")));

121

StringBuilder sb = new StringBuilder(); String line = null;

while ((line = br.readLine()) != null) { sb.append(line + "\n");

}

String str = new String(sb.toString()) ;

} catch (IOException e) {

e.printStackTrace();

}

Parsing du .json

// aller chercher la valeur du nom

// dans l’objet bix, l’Array promenade et l’objet nom

122

JSONObject jObjConnection = new JSONObject(str); JSONObject jsonBix = jObjConnection.getJSONObject("bix"); JSONArray jsonA=jsonBix.getJSONArray("promenade");

for(int i =0 ; i < jsonA.length() ; i++) {

JSONObject msg = (JSONObject)(i) ;

JSONObject nomObj = msg.getJSONObject("nom") ;

String nom = nomObj.getString("-val");


 

Connection Internet

¨   Rajouter dans le AndroidManifest une user permission

124

<uses-permission android:name="android.permission.INTERNET"/>

Remarque : sert à décrire l’application et définir ses permissions

¨   Interdiction de faire un appel réseau dans le thread principal -> NetworkOnMainThreadException

Il va donc falloir lancer une tâche parallèle, plusieurs techniques existent

Connection Internet

try {

URL url = new URL(getString(R.string.ipDabatabase)) ;

// http connection

HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); try {

InputStream in = new BufferedInputStream(urlConnection.getInputStream());

// conversion en String d’un fichier codé en UTF8. ‘\\A’ : début du fichier

125

String inputStreamString = new Scanner(in,"UTF-8").useDelimiter("\\A").next(); // à coder ce qu’on fait de la String

} finally { urlConnection.disconnect();

}

} catch (MalformedURLException e){

Log.d(TAGS, "Prb URL database connection");

} catch (IOException e){

Log.d(TAGS, "Prb I/O database connection");

}

Les fichiers de données formatés

¨ Utilisation de .JSON (plus concis que .xml)

126

{

"ballades": [

{

"id": "12",

"name": "Le Fantôme du Sémaphore",

"file": "balade_canelet.mp3"

},

{

"id": "19",

"name": "Frioul",

"file": "balade_frioul.mp3"

}

]

}

Parsing d’un fichier JSON

JSONObject jObjConnection = null ;

try {

jObjConnection = new JSONObject(inputStreamString );

127

JSONArray jsonA=jObjConnection.getJSONArray( "ballades");

String msg = jObjConnection.getJSONObject("msg").getString("reason");

Log.d(, msg);

} catch (JSONException e) {

Log.e("JSON Parser", "Error parsing data " + e.toString());

}

 

Les services

¨   Les services s’executent de manière invisible

¨   On peut les créer les redémarrer, les arrêter, les lier à des activités

¨   Mécanisme complet et assez compliqué, il est préférable d’utiliser les autres technique si cela est possible : IntentService, AsyncTask

¨   Doivent être déclarés dans le manifest en tant que fils de l’application

<service android:enabled="true" android:name= "MyService" android:permission=".MY_SERVICE_PERMISSION"/>

Démarrer un service

¨ Lancer une intention explicite avec le nom du service.

Intent intent = new Intent(, MyService.class);

// rajouter des paramètres dans l’intent startService(intent);

IntentService

¨   Classe la plus pratique pour une tâche simple avec création d’un Thread asynchrone

¨   Passage des paramètres par un Intent

¨   Mise en place automatique d’une gestion de file d’attente des Intent

¨   LIMITATION : pas de communication retour avec la classe qui a créer l’intentService.

->A n’utiliser que pour un service indépendant


Classe qui hérite de IntentService

¨ Définit l’action à réaliser dans un thread indépendant public class ConnectIntentService extends IntentService {

/**…*/

public ConnectIntentService() {

super(MainActivity.TAGS) ;

}

/** méthode déclenchée par l’intention */

@Override protected void onHandleIntent(Intent intent) {

// récupération d’un paramètre de l’intent

String user = intent.getStringExtra(MainActivity.EXT_MSG_USER);

Déclenchement de l’IntentService

¨ Création d’un intent, ajout de paramètres, démarrage du service

// création de l’intention explicite pour le service

Intent intent = new Intent(MainActivity.this, ConnectIntentService.class);

// ajout des paramètres intent.putExtra(EXT_MSG_USER, “Bob”);

// démarrage du service startService(intent);

AsyncTask

¨   Création d’un thread pour réaliser une tâche avec gestion de la fin, de la progression

¨   Possibilité d’interagir avec le thread de l’IHM

¨   Encore mieux : AsyncTaskLoader

¨   Création d’une classe qui hérite de AsyncTask qui surcharge

¤  doInBackground() // la tâche à réaliser hors thread IHM

¤  onPostExecute() // après la tâche : dans thread IHM

¤  onPreExecute() // facultatif : à faire dans l’IHM avant la tache

¤  onProgressUpdate() // facultatif : gestion progression

¨   Les params peuvent être de nombre variable (mais même type)

¨   Les params passés à la tâche, la valeur retournée par celle-ci et la progression peuvent être facultatifs.

AsyncTask

private class ConnectAsyncTask extends AsyncTask<String, Void , String> { /** executed in threads created specially for the task */ protected String doInBackground(Stringparameter) {// n params …

String result = null ;

//  ex : result = parameter[Ø]+" work so hard"; return result ;

}

/** executed in UI thread */

protected void onPostExecute(String result) {

… // ex : display resullt in a Toast }

}

// ailleurs, création de la task new ConnectAsyncTask().execute(String1, String2, String3…);    // n params

AsyncTask : gestion de la progression

private class ConnectAsyncTask extends AsyncTask<String, String, String> {

/** executed in threads created specially for the task */ protected String doInBackground(String…parameter) {

publishProgress( "On démarre à peine"); // call onProgressUpdate

… publishProgress( "On vient de passer la moitié…"); // call onProgressUpdate

}

/** called by publishProgress, executed in the UI */ protected void onProgressUpdate(String msgs) { Log.d(, msgs[0]);

}

AsyncTask : résumé

¨   You can specify the type of the parameters, the progress values, and the final value of the task, using generics

¨   The method doInBackground() executes automatically on a worker thread

¨   onPreExecute(), onPostExecute(), and onProgressUpdate() are all invoked on the UI thread

¨   The value returned by doInBackground() is sent to onPostExecute()

¨   You can call publishProgress() at anytime in doInBackground() to executeonProgressUpdate() on the UI thread

¨   You can cancel the task at any time, from any thread (myTask.cancel(true);)


Loaders

¨  Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:

139

¨  They are available to every Activity and Fragment.

¨  They provide asynchronous loading of data.

¨  They monitor the source of their data and deliver new results when the content changes.

¨  They automatically reconnect to the last loader's cursor when being recreated after a configuration change.

Thus, they don't need to re-query their data.


 

Building a Dynamic UI

141

Un fragment est une sorte de sous activité que l’on peut ajouter ou supprimer statiquement ou dynamiquement dans une activité.

Les fragments ont été instaurés pour gérer les tailles des mobiles+tablettes :

 

142

Building a Dynamic UI with

¤ Utilisation de layouts (.xml) différents en fonction de la taille de l’écran pour afficher 1 ou  2 fragment.

¨   Sur grand écran (tablette) : layout de l’Activité A contient les 2 fragments. Activité B jamais utilisée

¨   Sur petit écran (mobile), layout Activity A contient uniquement le Fragment A (ListView). On doit lancer l’activité B pour voir le 2nd fragment

¨   Quand on sélectionne un élément de la liste (Frag A), si grand écran, on notifie le fragment B de la sélection, si petit écran on lance activité B

Quelques règles

¨  Un fragment ne communique jamais directement avec un autre fragment mais passe par l’activité.

143

¨  Faire attention à mettre le code dans les fragments et non dans l’activité (modularité)

¨  Une interface doit être déclarée dans chaque fragment pour que l’activité implémente un système de callback

Cycle de vie d’un fragment

¨   Cycle de vie sur le même modèle qu’une Activity

144

onCreate() : initialisation du fragment, y mettre les créations des objets onCreatView () : création de l’IHM onActivityCreated() : appelé quand l’Activity mère et son IHM sont construites. On peut alors interagir avec. onPause () : appelé quand le segment disparait

¨   Les fragments suivent le cycle de vie de l’Activité mère : pause/stop/destroy

Exemple de Fragment

¨   Exemple affichant la liste des promos d’étudiants. Le détail de la promo quand un élément est sélectionné

145

¨   Sur grand écran (tablette et écran horizontale) la liste et le détail de l’élément sont affichés (ListFragment et EtudiantFragment)

¨   Sur écran vertical de tél, seule la liste est affichée (ListFragment) quand on sélectionne, le détail est affiché en plein écran (EtudiantFragment).

¨   Nécessite 4 classes : FragmentMainActivity, ListPromoFragment, EtudiantActivity, EtudiantFragment

¨

¨   Nécessite 6 fichiers .xml de layout

Activity englobant les Fragment (1/4) : FragmentMainActivity

public class FragmentMainActivity extends FragmentActivity implements OnItemSelectedListener { // interface à définir static String POSITION_MSG = "Position";

@Override protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState); setContentView(R.layout.fragment_activity_main);

146

}

/** callback appelé par list fragment quand on selectionne un élt */ public void onItemSelected(int position) {

EtudiantFragment etudiantFrag =(EtudiantFragment)getSupportFragmentManager().findFragmentById(R.id.etudiantFrag); if (etudiantFrag == null || ! etudiantFrag.isInLayout()) { // EtudiantFragment is not in the layout (handset layout),

// so start EtudiantActivity and pass it the info about the selected item Intent intent = new Intent(this, EtudiantActivity.class); intent.putExtra(POSITION_MSG, position); startActivity(intent);

} else // EtudiantActivity is in layout (tablet layout), tell the fragment to update

etudiantFrag.updateContent(position);

} }

Layouts englobant les 2 Fragments

¨ pour avoir 2 layouts identiques : layout-land/layout-large

147

¤  création d’un fichier layout unique : layout/

¤  Création de 2 fichiers incluant le précédant n layout-land/ n

Eclipse : pour créer les xml, new…> Other > XML Android File

 

Layouts englobant les 2 Fragments

148

: layout/

<fragment android:name="ismin.M_S13.trombismino.ListPromoFragment" // !!! CHANGER android:id="@+id/listPromo" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" />

<fragment android:name="ismin.M_S13.trombismino.EtudiantFragment"// !!! CHANGER android:id="@+id/etudiantFrag" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" />

</LinearLayout>

149

Layouts incluant layout-land/

<?xml version="1.0" encoding="utf-8"?> <merge>

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

</merge>

Layout pour un téléphone en portrait

<LinearLayout xmlns:android="; android:baselineAligned="false" xmlns:tools="; android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" >

150

<fragment android:id="@+id/listPromo" android:name="ismin.M_S13.trombismino.ListPromoFragment » // !!! CHANGER package

android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginLeft="@dimen/activity_left_margin" android:layout_marginRight="@dimen/activity_right_margin" android:layout_weight="1" tools:context=".FragmentMainActivity"

tools:layout="@android:layout/list_content" />

</LinearLayout>


ListPromoFragment

¨   Classe spécialement conçue pour visualiser les listes dans un fragment

151

¤ Équivalent des ListActivity (Activité avec ListView) pour les fragments

¨   Exemple : visualisation d’un tableau de String sous forme de liste (cf fig)

¨   Doit définir une interface pour décrire la méthode que l’activity doit exécuter quand on clique

¨   La FragmentMainActivity gère le redispatching aux autres fragments de la gestion de l’évènement

 

ListPromoFragment

/** Creation of the View */ public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {  setListAdapter(new ArrayAdapter<String>(getActivity(),

android.R.layout.simple_list_item_activated_1, nameUsers)); //toDo avec AsyncTask dans onCreate

return super.onCreateView(inflater, container, savedInstanceState); 

ListPromoFragment

public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnItemSelectedListener) activity;

153

} catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnItemSelectedListener");

}

}

@Override

public void onListItemClick(ListView l, View v, int position, long id) { mListener.onItemSelected(position); // Send the position to the host activity }

}


Fabrication d’un Fragment (3/4) EtudiantFragment

public class EtudiantFragment extends Fragment { private TextView logView ;

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.etudiant_view, container,

154

false); logView =(TextView)view.findViewById(R.id.logView);// store view for

later

return view;

}

/**  update the content corresponding to the selected index */ void updateContent(int position) { logView.setText("Selected :" +position); }

}

Layout de d’étudiant : layout/

155

<RelativeLayout xmlns:android="; xmlns:tools="; android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" >

<TextView android:id="@+id/titleView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@style/port_bigTxt" android:text="@string/msg_title" />

<TextView android:id="@+id/logView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/titleView" android:layout_centerHorizontal="true" android:layout_marginTop="112dp" android:text="@string/etudiant_nom" />

</RelativeLayout>

Fabrication de l’activité du Fragment (4/4)

EtudiantActivity

public class EtudiantActivity extends FragmentActivity {

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

if(getResources().getConfiguration().orientation== Configuration.ORIENTATION_LANDSCAPE){

156

// If  landscape mode, we don't need this activity.

finish(); return;

} else if (savedInstanceState == null) { setContentView(R.layout.fragment_activity_etudiant); EtudiantFragment etudiantFragment =

(EtudiantFragment)getSupportFragmentManager().findFragmentById(R.id.etudiantFrag); int indexSelected

=getIntent().getIntExtra(FragmentMainActivity.POSITION_MSG,0); etudiantFragment.updateContent(indexSelected); }

}

Layout de de l’activity de l’étudiant :

157

<LinearLayout xmlns:android="; xmlns:tools="; android:baselineAligned="false" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" >

<fragment android:id="@+id/etudiantFrag"

android:name="ismin.M_S13.trombismino.EtudiantFragment" // !!! CHANGER package android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginLeft="@dimen/activity_left_margin" android:layout_marginRight="@dimen/activity_right_margin" tools:context=".EtudiantActivity" android:layout_weight="1"/>

</LinearLayout>

Quelques classes héritées de fragments

158

¨  DialogFragment : displays a floating dialog. Using this class to create a dialog is a good alternative to using the dialog helper methods in theActivityclass, because you canincorporate a fragment dialog into the back stack offragments managed by the activity, allowing the user to returnto a dismissed fragment.

¨  ListFragment : displays a list of items that are managed by an adapter (such as a SimpleCursorAdapter), similar to ListActivity. It provides several methods for managing a listview, such as the onListItemClick() callback to handle clickevents.

¨  PreferenceFragment : displays a hierarchy of Preference

objects as a list, similar to PreferenceActivity. This is usefulwhen creating a "settings" activity for your application.

Gestion dynamique des fragments

159

Il est possible de gérer dynamiquement la création (add), la destruction (remove) , le changement de fragments (replace), en utilisant le FragmentManager,  en démarrant une transaction et en faisant un commit() après l’action. Il est possible de rajouter des animations à ces transactions (setTransition ou setCustomAnimations)

FragmentTransaction ft = getFragmentManager().beginTransaction(); Ft.setTransition(FragmentTransaction.TRANSMIT_FRAGMENT_OPEN) ; ft.replace(R.id.my_idFrag, new MyFragment()); ft.commit();

160

Infos sur les fragments

Un fragment n’a pas nécessairement d’UI

Un fragment peut utiliser les transitions


 
 

Préférences partagées

¨  SharedPreferences : paires nom/valeur dans le contexte d’application

¨  Créer, éditer et sauvegarder

163

Final static String LOGIN_PREFS = "login_param" ;

// création preferences ou ouverture si existe

SharedPreferences sharedPref =      

getSharedPreferences(LOGIN_PREFS, Activity.MODE_PRIVATE); SharedPreferences.Editor editor = (); editor.putString("user", "bob"); editor.commit(); // validate changes

Récupérer les valeurs

¨  Récuperer la valeur de la clef user

// preferences

SharedPreferences sharedPref =      

164

getSharedPreferences(LOGIN_PREFS, Activity.MODE_PRIVATE);

// get user value,  "" if undefined

final String userPref = sharedPref.getString("user", "");

¨  Tester si une clef existe

aller voir dans la doc de l’API la méthode contains


 

Base de données, moteur SQLite

¨  => SQLiteOpenHelper : classe abstraite à implémenter ; modèle pour la création, l’ouverture et la Mise à Jour

166

¨  La création est codée dans SQLiteOpenHelper

¨  Une fois crée on demande la SQLiteDatabase au

SQLiteOpenHelper soit pour la lecture soit pour l’écriture

¨  ContentValues : lignes à insérer dans la base

¨  Cursor : pointeurs vers les résultats d’une requête

BalladesDBOpenHelper (1/2)

/** classe de creation de la base */

public class BalladesDBOpenHelper extends SQLiteOpenHelper {

167

private static BalladesDBOpenHelper db ; private final static String DATABASE_NAME = "" ; final static String DATABASE_TABLE = "Ballades" ; private final static int DATABASE_VERSION = 1 ;

// def des champs de la base public static final String COLUMN_ID = "_id"; // id public static final String COLUMN_NAME = "name"; public static final String COLUMN_PLAY = "play_file";

// Database creation sql statement private static final String DATABASE_CREATE = "create table "

+ DATABASE_TABLE + "(" +

COLUMN_ID + " integer primary key autoincrement, " +

COLUMN_NAME + " text not null, " +

COLUMN_PLAY + " text not null);";

BalladesDBOpenHelper (2/2)

public static BalladesDBOpenHelper getBalladesDBOpenHelper(Context context) {//Singleton if(db==null)

db = new BalladesDBOpenHelper(context) ;

return db ;

}

private BalladesDBOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION);

168

}

/** creation de la base, est appelée quand on cherche à lire ou écrire dans la base*/

@Override public void onCreate(SQLiteDatabase db) {

db.execSQL(DATABASE_CREATE); }

/** update à une nouvelle version de la base */

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(BalladesDBOpenHelper.class.getName(),

"Upgrading database from version " + oldVersion + " to "

+ newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE); onCreate(db);

}

}

Insertion dans la base : ContentValues

// open database to write => appel à onCreate

SQLiteDatabase db =

BalladesDBOpenHelper.getBalladesDBOpenHelper( getBaseContext()).getWritableDatabase() ;

// getBaseContext ou getContext/this/getActivity

169

// build ContentValues with data

ContentValues values = new ContentValues(); (BalladesDBOpenHelper.COLUMN_NAME, "ballade1"); (BalladesDBOpenHelper.COLUMN_PLAY, "ballades1.mp3" );

// insert in db

long id =db.insert(BalladesDBOpenHelper.DATABASE_TABLE, null, values);

Récupération des data: Cursor

String [] allColumns = {BalladesDBOpenHelper.COLUMN_ID,

BalladesDBOpenHelper.COLUMN_NAME,

BalladesDBOpenHelper.COLUMN_PLAY} ;

// open database to read

SQLiteDatabase dbR =

BalladesDBOpenHelper.getBalladesDBOpenHelper(getBaseContext()).getReadableDat abase() ;

170

Cursor cursor = dbR.query(BalladesDBOpenHelper.DATABASE_TABLE, allColumns, null, null, null, null, null);

cursor.moveToFirst(); while (!cursor.isAfterLast()) {

Log.d(TAG,"id = "+cursor.getLong(0) +

", name = "+cursor.getString(1) +

", play= "+cursor.getString(2));

cursor.moveToNext();

}

// make sure to close the cursor cursor.close();

Update d’une valeur

String [] allColumns = {BalladesDBOpenHelper.COLUMN_ID,

BalladesDBOpenHelper.COLUMN_NAME,

171

BalladesDBOpenHelper.COLUMN_PLAY} ;

// open database to read

SQLiteDatabase db =BalladesDBOpenHelper.getBalladesDBOpenHelper(getBaseContext()).getWritableDatabase();

ContentValues values = new ContentValues(); (BalladesDBOpenHelper.COLUMN_NAME, " new name ");

db.update(BalladesDBOpenHelper.DATABASE_TABLE, values, BalladesDBOpenHelper.COLUMN_ID + "=" + rowId, null);

// make sure to close the cursor cursor.close();


 
 
 

Création d’un Content Provider

1.      Création d’une classe qui hérite de ContentProvider

175

2.      Implémentation de toutes les méthodes de la classe abstraite ContentProvider

n Création de la base n Implémentation des requètes

3.      Déclaration du provider dans AndroidManifest pour le rendre visible à toutes les Activity


extends Content Provider (1/…)

Héritage d’une classe abstraite

-> surcharge obligatoire de onCreate, query, update, delete, insert, getType

public class AnciensContentProvider extends ContentProvider {

176

/**

*   Create the database of the Anciens with singleton for latter

*   @see android.content.ContentProvider#onCreate()

*/

@Override

public boolean onCreate() {

//use a singleton : méthode à créer dans AnciensDBOpenHelper database = AnciensDBOpenHelper.getAnciensDBOpenHelper(getContext()); return false;

}

extends Content Provider (2/…)

¨ Exposition de l’URI de l’ensemble des éléments

177

private static final String AUTHORITY="fr.ismin.M_S13.provider_anciens"; // == MANIFEST private static final String BASE_PATH = "elements"; public static final Uri CONTENT_URI = Uri.parse( "content://"+AUTHORITY+"/"+BASE_PATH);

Info : les éléments seront accessibles individuellement avec

"content://"+AUTHORITY+"/"+BASE_PATH+"/1"

Création d’un URIMatcher pour reconnaître le type d’URI

private static final int ALLROWS= 1; private static final int SINGLE_ROW = 2;

private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

static {

uriMatcher.addURI(AUTHORITY, BASE_PATH, ALLROWS); uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", SINGLE_ROW ); }

query()

/* execution de la requete SQL avec filtrage de l’URI en fonction de l’’uriMatcher… public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); queryBuilder.setTables(AnciensDBOpenHelper.DATABASE_TABLE);

178

switch (uriMatcher.match(uri)) { case ALLROWS: break;

case SINGLE_ROW:

// adding the ID to the original query queryBuilder.appendWhere(AnciensDBOpenHelper.COLUMN_ID + "="+ uri.getLastPathSegment());

break;

default: throw new IllegalArgumentException("Unknown URI: " + uri);

}

SQLiteDatabase db = database.getWritableDatabase();

Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);

return cursor;

}


getType() -> MIME (3/…)

/**

*return the good MIME type

* @see android.content.ContentProvider#getType()

*/

@Override

179

public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case ALLROWS: return "; ; case SINGLE_ROW: return ";

; default: throw new IllegalArgumentException("Unknown URI: " +

uri);

}

}

delete()

public int delete(Uri uri, String selection, String[] selectionArgs) {

SQLiteDatabase sqlDB = database.getWritableDatabase() ; String rowId = uri.getLastPathSegment() ; int rowsDeleted = 0; switch (uriMatcher.match(uri)) { case ALLROWS: rowsDeleted=sqlDB.delete(AnciensDBOpenHelper.DATABASE_TABLE, selection, selectionArgs);

180

break; case SINGLE_ROW: if (TextUtils.isEmpty(selection)) { rowsDeleted = sqlDB.delete(AnciensDBOpenHelper.DATABASE_TABLE,

AnciensDBOpenHelper.COLUMN_ID + "=" + rowId, null);

} else {

rowsDeleted = sqlDB.delete(AnciensDBOpenHelper.DATABASE_TABLE,

AnciensDBOpenHelper.COLUMN_ID + "=" + rowId

+ " and " + selection, selectionArgs);

}

break; default: throw new IllegalArgumentException("Unknown URI: " + uri);

} getContext().getContentResolver().notifyChange(uri, null); return rowsDeleted; }

définition insert()

/** insert one element*/

public Uri insert(Uri uri, ContentValues values) {

181

SQLiteDatabase sqlDB = database.getWritableDatabase(); long id = 0; switch (uriMatcher.match(uri)) { case ALLROWS:



id=sqlDB.insert(AnciensDBOpenHelper.DATABASE_TABLE, null, values); break; default:

throw new IllegalArgumentException("Unknown URI:" +

uri); }

getContext().getContentResolver().notifyChange(uri, null); return Uri.parse(BASE_PATH + "/" + id); }


définition update()

/** update element(s) */

public int update(Uri uri, ContentValues values, String selection,

String[] selectionArgs) {

182

SQLiteDatabase sqlDB = database.getWritableDatabase(); int rowsUpdated = 0; switch (uriMatcher.match(uri)) { case SINGLE_ROW:

String rowId = uri.getLastPathSegment();

selection =  AnciensDBOpenHelper.COLUMN_ID + "=" +

(!TextUtils.isEmpty(selection) ?" AND ("+selection+')':"");

default : break;

}

int updateCount = sqlDB.update(AnciensDBOpenHelper.DATABASE_TABLE, values, selection, selectionArgs);

getContext().getContentResolver().notifyChange(uri, null); // notify return updateCount; }

2) Déclaration AndroidManifest

¨   Définition de l’application  :AnciensContentProvider

¨   Définition de l’autorité du fournisseur en reprenant l’esprit du package pour le nom :

183

fr.ismin.M_S13.provider_anciens

<application

<!– A placer après les Activity -->

<provider android:name=".AnciensContentProvider"

android:authorities= "fr.ismin.M_S13.provider_anciens"> </provider> </application>

Se servir du ContentProvider:

ajout d’une donnée

/** ajout d’un element */

// get the content resolver

184

ContentResolver cr = getContentResolver() ;

Log.d(TAGS,"CProv="+cr.getType(AnciensContentProvider.CONTENT_URI) );

ContentValues values = new ContentValues(); (AnciensDBOpenHelper.COLUMN_NAME, "Sinclar"); (AnciensDBOpenHelper.COLUMN_FORNAME, "bob");

Uri uri = cr.insert(AnciensContentProvider.CONTENT_URI, values);

Accéder au content : récupération des données

String []allColumns = {AnciensDBOpenHelper.COLUMN_ID,

AnciensDBOpenHelper.COLUMN_NAME, AnciensDBOpenHelper.COLUMN_FORNAME};

// recup d’un cursor

185

Cursor cursor = cr.query(AnciensContentProvider.CONTENT_URI,allColumns, null, null, null);

Log.d(TAGS, "cursor="+cursor); cursor.moveToFirst();

while (!cursor.isAfterLast()) { // parcours de tous les elts

Log.d(TAGS, "id = "+cursor.getLong(0)+

", name = "+cursor.getString(1)+ ", forname = "+cursor.getString(2));

cursor.moveToNext();

}

// make sure to close the cursor cursor.close();


 
 

Etapes

             1.      Créer le fichier des Méta-données

()

188

2.       Insérer l’outil de recherche dans une vue 3.     Déclarer la Searchable Activity

(Optionnel) Suggestions de recherche

4.      Recevoir la requête de recherche

5.      Chercher les données

6.      Afficher les résultats

1) Meta-données à définir

¨ Configuration du search dialog ou widget :

¤ Texte search_hint, voice search, search suggestion…

189

¨ Créer un fichier

<?xml version="1.0" encoding="utf-8"?>

<searchable

xmlns:android=" d"> android:label="@string/app_name" android:hint="@string/search_hint"

</searchable>

2) Ajout du Widget de Recherche

¨ Ajout du Widget dans la vue_souhaitée.xml   

190

<SearchView android:id="@+id/searchView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" > </SearchView>

(Optionnel) Suggestions de recherche

              •   Modifier : android:searchSuggestAuthority

191

<?xml version="1.0" encoding="utf-8"?>

<searchable xmlns:android="; android:label="@string/app_label" android:hint="@string/search_hint » android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider">

</searchable>

192

(Optionnel) Suggestions de recherche


3) Déclarer la Searchable Activity

¨   Activité qui gère la recherche et qui l’affiche -> modif du Manifest

1.    Declare the activity to accept the ACTION_SEARCHintent, in an <intent-filter>element.

2.    Specify the searchable configuration to use, in a <meta-data>element.

¨   Manifest

193

<application >

<activity android:name=".SearchableActivity" >

<intent-filter>

<action android:name="android.intent.action.SEARCH" />

</intent-filter>

<meta-data android:name=".searchable" android:resource="@xml/searchable"/> </activity>

</application>

4) Recevoir la requête de recherche

194

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.search);

// Get the intent, verify the action and get the query

Intent intent = getIntent();

if (Intent.ACTION_SEARCH.equals(intent.getAction())) {

String query = intent.getStringExtra(SearchManager.QUERY); doMySearch(query);

}

}

 

Créer une nouvelle activity Google Map

¨  Créer l’activity

File/New/Google/Google Map Activity

¨  Récupérer la Google Map API Key

-> Dans , cliquez sur le lien automatiquement généré

Ex :

¤  Choisir Créer un projet

¤  Cliquer sur créer pour générer votre clef, exemple AIzaSyDU3LIKUNqFs9jru0vJLqDgChKnc7cNgxw

¨  il faut mettre la bonne clef dans

À la place de YOUR KEY HERE

Manifest :Insertion de la clef et des permissions

¨ Fait automatiquement dans le Manifest:

1.    Ajout de la Key

<application

<meta-data

android:name=".API_KEY"

android:value="@string/google_maps_key"/>

2.    Ajout des permissions pour un accès fin à la position

<uses-permission

android:name="android.permission.ACCESS_FINE_LOCATION" />

<uses-permission

android:name="android.permission.ACCESS_COARSE_LOCATION" />

Création de la Map

¨  Le Wizard a automatiquement crée une Activity Map

¤ l

¤

¨  Dans notre cas, Il faut le transformer en Fragment

¤ Le plus simple est de créer MapsFragment : un Blank Fragment sans créer layout XML et transformer son .java


[1/2]Ajout d’une Map dans 1 Fragment (au lieu d’Activity)

import .Fragment;

public class MapFragment extends Fragment implements OnMapReadyCallback {

201

// onCreateView qui retourne une map public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { return inflater.inflate(R.layout.activity_maps, container, false);

}

private final static LatLng GARDANNE = new LatLng(43.455669,5.47064899999998) ;

@Override

public void onActivityCreated (Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState);

SupportMapFragment supportMapFragment = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)); supportMapFragment.getMapAsync(this); }

[2/2] Ajout d’une Map dans 1 Fragment (au lieu d’Activity)

@Override

202

public void onMapReady(GoogleMap googleMap) { googleMap.animateCamera(CameraUpdateFactory.newLatLng(GARDANNE), 20000, null); if (ActivityCompat.checkSelfPermission(getActivity().getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED

&& ActivityCompat.checkSelfPermission(getContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION)

!= PackageManager.PERMISSION_GRANTED) {

return;

} else

googleMap.setMyLocationEnabled(true);

}

Quelques modifs

¨   , supprimer

tools:.context="balades.isen16ballades.MapsActivity"

¨   MainActivity, ajouter le listener d’interaction avec le fragment :

Class MainActivity … implements FragmentMap.OnFragmentInteractionListener,…

Dans son tél, donner les autorisations pour la localisation

Config. MapFragment or a MapView par XML

•mapType. This allows you to specify the type of map to display. Valid values include: none, normal, hybrid, satellite and terrain.

•cameraTargetLat, cameraTargetLng, cameraZoom, cameraBearing, cameraTilt. These allow you to specify the initial camera position. Seehere for more details on Camera Position and its properties.

204

•uiZoomControls, uiCompass. These allow you to specify whether you want the zoom controls and compass to appear on the map. See UiSettingsfor more details.

•uiZoomGestures, uiScrollGestures, uiRotateGestures, uiTiltGestures. These allow you to specify which gestures are enabled/disabled for interaction with the map. See UiSettings for more details.

•zOrderOnTop. Control whether the map view's surface is placed on top of its window. See

SurfaceView.setZOrderOnTop(boolean) for more details. Note that this will cover all other views that could appear on the map (e.g., the zoom controls, the my location button).

•useViewLifecycle. Only valid with a MapFragment. This attribute specifies whether the lifecycle of the map should be tied to the fragment's view or the fragment itself. See here for more details.

In order to use these custom attributes within your XML layout file, you must first add the following namespace declaration (you can choose any namespace, it doesn't have to be map):

xmlns:map=";


Exemple configuration

¨ <?xml version="1.0" encoding="utf-8"?>

<fragment xmlns:android="; xmlns:map="; android:name=".SupportMapFragment" android:id="@+id/map"

android:layout_width="match_parent" android:layout_height="match_parent" map:cameraBearing="112.5" map:cameraTargetLat="43.296482" map:cameraTargetLng="5.369779999999992" map:cameraTilt="30" map:cameraZoom="13" map:mapType="normal" map:uiCompass="false" map:uiRotateGestures="true" map:uiScrollGestures="true" map:uiTiltGestures="true" map:uiZoomControls="true" map:uiZoomGestures="true"/>

L. Freund

Exemple simple dans une Activity : déplacement Caméra

Immédiat : moveCamera( ), animation : animateCamera()

206

public class MapAnciensActivity extends Activity { private GoogleMap mMap; private final static LatLng GARDANNE = new LatLng(43.455669,5.47064899999998) ;

public void onCreate(Bundle savedInstance){

super.onCreate(savedInstance); setContentView(R.layout.fragment_map);

mMap= ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();

if (mMap != null) { mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); mMap.animateCamera(CameraUpdateFactory.newLatLng(GARDANNE),2000, null);

}

}

}

Placement d’un Marker

¨   Du marqueur simple

mMap.addMarker(new MarkerOptions().position(GARDANNE));

¨   Au plus custom

googleMap.addMarker(new MarkerOptions()

.position(GARDANNE)

.title("Gardanne")

.snippet("Population: beaucoup trop")

.alpha(0.7f)

.icon(BitmapDescriptorFactory.fromResource(R.drawable.lampe_transp))

);

¨   Possibilité de rajouté des listener de click et de Drag

GoogleMap.setOnMarkerClickListener(OnMarkerClickListener), GoogleMap.setOnMarkerDragListener


Dessin de Shape

¨ Polyline, Polygon, Circle

// exemple Cercle

CircleOptions circleOptions = new CircleOptions()

.center(GARDANNE)

.fillColor(Color.parseColor("#440000DD"))

.radius(500); // In meters // Get back the mutable Circle

Circle circle = mMap.addCircle(circleOptions);

Remarque : pour dessiner une image collée à la map et qui varie en fonction du zoom, de la rotation, on utilisera une GroundOverLay

Location Data

¨ Il est possible d’utiliser le Location Layer pour se positionner en appuyant sur l’icône 

mMap.setMyLocationEnabled(true);

Par contre la version de la target doit être inférieure ou égale à 22 car sinon il faut demander dynamiquement les permissions à l’utilisateur

Utilisation du géocodeur

¨   Le Geocoder fait partir des Google play Services

¨   Géocodage Avant : lat,long -> add

¨   Géocodage Arrière: add -> lat,long

Utilisation du Add -> lat, long

¨ A placer dans une AsyncTask

List <Address> locations = null ;

// utilisation du geocodage

Geocoder geocoder = new Geocoder(getApplicationContext(),

Locale.getDefault());

try { locations = geocoder.getFromLocationName("15 rue paradis, marseille", 10);

Log.d(,"["+location.getLatitude()+ ", "

+location.getLongitude()+"]");

}catch(IOException e){

Log.e(,"IO Exception", e); }



 
 
 

Xml pour la description

Créer res/xml/ qui servira à décrire le wallpaper :

215

<?xml version="1.0" encoding="UTF-8"?>

<wallpaper xmlns:android="; android:thumbnail="@drawable/ic_launcher" android:description="@string/wallpaper_description"/> Rajouter dans

<string name="wallpaper_description">WallPaper LogoIsmin (2013)</string>


Modification du

¨ Rajouter après android:theme="@style/AppTheme" >

<service android:label="@string/wallpaper_service" android:name=".WallPaperLogoIsmin" android:permission="android.permission.BIND_WALLPAPER">

<intent-filter>

<action android:name="android.service.wallpaper.WallpaperService"></action>

</intent-filter>

<meta-data android:name="android.service.wallpaper"

android:resource="@xml/wallpaperdescr"></meta-data>

</service>

¨ Rajouter dans

<string name="wallpaper_service ">Logo Ismin</string>

Rajouter la classe interne :

WallPaperEngine

/** Engine for displaying WallPaper */ private class WallpaperEngine extends Engine {

private Bitmap lampPic = BitmapFactory.decodeResource(getResources(), R.drawable.lampe_transp);

/** Get the Canvas and draw*/

public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {

super.onSurfaceChanged(holder, format, width, height); Canvas canvas = null; try {

canvas = holder.lockCanvas(); if (canvas != null) {

canvas.drawColor(Color.LTGRAY);

canvas.drawBitmap(lampPic, (width - lampPic.getWidth())/2, (height - lampPic.getHeight())/2, null);

}

} finally {

if (canvas != null)

holder.unlockCanvasAndPost

                                                                }                                                             L. Freund 19/04/2019(canvas);

}}

Faire une image animée

Rajouter un Handler, un Runnable, un onTouchEvent


vos ventes mobiles

1. Redonnez du contrôle au consommateur

219

Il est très difficile de proposer de nombreux produits à la vue du consommateur sachant que l’écran d’un téléphone est relativement petit. Les utilisateurs mobiles s’attendent donc à ce que vous leur proposiez des filtres et des façons de trier les listings de produits en fonction de leurs besoins.

Par exemple, pour un site dédiée à la mode. Proposez des filtres en fonction : homme/femme/enfants, en fonction des tailles, en fonction des marques,… Proposez ensuite de classer les résultats par ordre de prix croissant ou décroissant, par popularité, par nouveauté,… Autant de possibilités qui permettront aux internautes d’aller plus vite dans leurs achats et donc de vivre une expérience de shopping agréable.

vos ventes mobiles

2. Simplifiez les formulaires d’inscription pour commander Si vous souhaitez éviter de perdre trop de clients lors de la première inscription pour passez commande, évitez les nombreux champs à remplir dans es formulaires.

220

Il est urgent de se focaliser sur l’optimisation des formulaires afin de booster la première étape du tunnel d’achat.

Certains éléments peuvent largement être améliorés :

La mise en majuscule automatique de la première lettre cause de nombreuses erreurs de création de compte et d’authentification Oubliez les listes avec 100 entrées parmi lesquelles choisir (exemple : choisir votre pays parmi la liste proposée)

Des champs inutiles au premier abord pour le consommateur : date de naissance par exemple.

vos ventes mobiles

3. Optimisez à 100% votre page de localisation

221

Les pages de localisation de votre point de vente physique, ou du point de vente où sont vendus vos produits sont souvent peu optimisées pour la lecture mobile. La plupart du temps, la carte de localisation défile dans tous les sens lorsque l’internaute souhaite scroller l’écran vers le bas. Il scrolle toute la carte au lieu de descendre simplement sur la page web. A une main cette manipulation est vraiment compliquée.

Faites en sorte que votre version mobile ne fasse apparaître que le minimum d’informations concernant la localisation. Faire apparaître une carte est une très bonne idée si cette dernière ne gâche pas l’expérience de navigation des internautes.

vos ventes mobiles

Soyez obsédé par la vitesse de chargement

222

Bien souvent, les sites mobiles sont très lents, même lorsque les internautes sont connectés au Wi-Fi. Il est urgent de revoir le poids de vos images, d’optimiser le CSS et le code Javascript car ce sont ces éléments en particulier qui allongent les temps de chargement des sites mobiles.

Les consommateurs sont de moins en moins tolérants face à des sites peu accessibles sur mobile. Ils ne vont pas crier au scandale mais vont simplement quitter le tunnel d’achat à peine entamé. Les conversions ne seront alors pas au rendez-vous. Le trafic provenant des devices mobiles n’est plus à prendre à la légère. Optimisez tous les canaux de conversion !


Couleurs principales

223

Matérial Design

@android:style/Theme.Material (dark version)

@android:style/Theme.Material.Light (light version)

@android:style/Theme.Material.Light.DarkActionBar

 

Définir son identité visuelle

=>

224

<?xml version="1.0" encoding="utf-8"?>

<resources>

<color name="colorPrimary">#3F51B5</color>

<color name="colorPrimaryDark">#303F9F</color>

<color name="colorAccent">#FF4081</color> </resources>

Prenez les couleurs de votre marque

225

Material Design


226

FloatingActionButton

Doc officielle

 

227

<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_dialog_email" />

// floating Button

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

fab.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

// Votre action

}

Interaction avec le Swipe du

228

ViewPager : zooming + fading

MainActivity implements ViewPager.OnPageChangeListener

… onCreate

mViewPager.addOnPageChangeListener(this);

@Override

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){

FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab); if (fab != null ) { if(positionOffset < 0.5) { fab.setScaleX(1 - 2*positionOffset); // zoom en fct position fab.setScaleY(1 - 2*positionOffset); // zoom en fct position fab.setAlpha(1 - 2*positionOffset); // alpha en fct position

} else { fab.setScaleX(positionOffset); fab.setScaleY(positionOffset); fab.setAlpha(positionOffset); }

}

}

@Override

public void onPageSelected(int position) {}

@Override

public void onPageScrollStateChanged(int state) {}

Les animations

229

¨  Avant 3.1(Milieu HoneyComb) View Animations définies dans : res/anim/

¨  Depuis 3.1(Milieu HoneyComb) Property Animations définies dans : res/animator/

¤ Permet d’animer des propriétés : plus complet

Animations avec 2 buttons

Créer

<FrameLayout xmlns:android="; xmlns:app="; android:layout_width="match_parent" android:layout_height="match_parent »>

230

<android.support.design.widget.FloatingActionButton android:id="@+id/fab_1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_menu_compass" android:visibility="invisible" app:backgroundTint="@color/colorFAB" app:fabSize="mini" />

<android.support.design.widget.FloatingActionButton android:id="@+id/fab_2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_menu_myplaces" android:visibility="invisible" app:backgroundTint="@color/colorFAB" app:fabSize="mini" />

</FrameLayout>

L’insérer dans le .xml au dessus du FAB existant

¨ <include layout="@layout/fab_layout"/>


Créer les animations :

apparition du button plus

Créer res/anim/

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="; android:fillAfter="true">

<!-- Rotate -->

<rotate android:duration="500" android:fromDegrees="30"

231

android:interpolator="@android:anim/linear_interpolator" android:pivotX="50%" android:pivotY="50%" android:repeatCount="4" android:repeatMode="reverse" android:toDegrees="0"></rotate>

<!--Move-->

<translate android:duration="1000" android:fromXDelta="170%" android:fromYDelta="25%"

android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate>

<!--Fade In-->

<alpha android:duration="2000" android:fromAlpha="0.0"

android:interpolator="@android:anim/decelerate_interpolator" android:toAlpha="1.0"></alpha>

</set>

Créer les animations :

disparition du button plus

Créer res/anim/

¨    <?xml version="1.0" encoding="utf-8"?>

232

<set xmlns:android="; android:fillAfter="true">

<!--Move-->

<translate android:duration="1000" android:fromXDelta="-170%" android:fromYDelta="-25%"

android:interpolator="@android:anim/linear_interpolator" android:toXDelta="0%" android:toYDelta="0%"></translate>

<!--Fade Out-->

<alpha android:duration="2000" android:fromAlpha="1.0"

android:interpolator="@android:anim/accelerate_interpolator" android:toAlpha="0.0"></alpha>

</set>

233

Modifier le pour déclencher les anims

//Animations

Animation show_fab_1 = AnimationUtils.loadAnimation(getApplication(), R.anim.fabplus_show); Animation hide_fab_1 = AnimationUtils.loadAnimation(getApplication(), R.anim.fabplus_hide);


SnackBar

234

¨  Permet l’affichage temporaire d’un message

¨  + Action si souhaité : 1 seule action

¨  Texte Only  

 

SnackBar

Snackbar snackbar = Snackbar

.make(findViewById(R.id.coordinatorLayout)

                                , "Message is deleted",      Snackbar.LENGTH_LONG)

.setAction("UNDO", new View.OnClickListener() {

235

@Override

public void onClick(View view) {

Snackbar snackbar1 = (coordinatorLayout,

"Message is restored!", Snackbar.LENGTH_SHORT); ();

} });

();

<android.support.design.widget.AppBarLayout

<android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimaryDark" />

236

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

mViewPager.setAdapter(fragsPagerAdapter);

TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(mViewPager);

¤   Pour ne pas avoir un Txt sur 2 lignes : scrollable app:tabMode="scrollable" ¤ Pour appliquer un style pour le tab style="@style/MyCustomTabLayout »

¤   Définir le style du tab, texte, indicator dans

<style name="MyCustomTabLayout" parent="Widget.Design.TabLayout">

237

<item name="tabMaxWidth">@dimen/tab_max_width</item>

<item name="tabIndicatorColor">?attr/colorAccent</item>

<item name="tabIndicatorHeight">2dp</item>

<item name="tabPaddingStart">12dp</item>

<item name="tabPaddingEnd">12dp</item>

<item name="tabBackground">?attr/selectableItemBackground</item>

<item name="tabTextAppearance">@style/MyCustomTabTextAppearance</item>

<item name="tabSelectedTextColor">?android:textColorPrimary</item>

</style>

<style name="MyCustomTabTextAppearance" parent="">

<item name="android:textSize">10sp</item>

<item name="android:textColor">@color/colorPrimary</item>

<item name="textAllCaps">false</item> </style>

CoordinatorLayout

238

¨  FrameLayout qui permet de regrouper des vues (Child) afin de coordonner leur déplacements en fonction du CoordinatorLayout (Parent)

¨  On peut utiliser le DefaultBehaviorou un autre

¤ Permet de gérer les éléments barre et les actions de scrolling associées

239

¤ A inclure dans un CoordinatorLayout

¤ Y ajouter un CollapsingToolbarLayout pour regrouper des éléments avec effet parallax

 

.xml

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout xmlns:android="; xmlns:app="; xmlns:tools="; android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="isen.guitare.MainActivity">

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

240

<android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="300dp" app:contentScrim="?attr/colorPrimary" android:fitsSystemWindows="true"

app:expandedTitleMarginEnd="@dimen/activity_horizontal_margin" app:expandedTitleMarginStart="@dimen/activity_horizontal_margin" app:layout_scrollFlags="scroll|exitUntilCollapsed">

<ImageView android:id="@+id/toolbar_image" android:layout_width="match_parent" android:layout_height="300dp" android:adjustViewBounds="true" android:contentDescription="@null" android:scaleType="centerCrop" android:src="@drawable/vanessa" app:layout_collapseMode="parallax"/>

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

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

<android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimaryDark" app:tabMode="scrollable" app:layout_scrollFlags="scroll" android:layout_gravity="bottom" style="@style/MyCustomTabLayout"

/>

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

                    <include        layout="@layout/content_main »/>

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

Explications

app:layout_scrollFlags

¨   scroll : ce flag doit être ajouté aux vues dont vous voulez qu’elles suivent le scroll du

CoordinatorLayout. Sans ce flag, les vues ne seront pas déplacées

¨   enterAlways : ce flag permet d’activer le quick-return de cette vue. Elle disparait avec le scroll up et réapparait lors du scroll down

241

¨   enterAlwaysCollapsed : Lorsque votre vue possède une minHeight et que vous utilisez ce flag, celle-ci va apparaître initialement avec sa taille minimale, puis va suivre le scroll pour atteindre sa taille finale (collapse -> visible)

¨   exitUntilCollapsed : ce flag, à l’inverse du enterAlwaysCollapsed, fait apparaître la vue à sa taille initiale, puis la réduit jusque sa minHeight lors du scroll (visible -> collapse)

app:layout_collapseMode

¨   pin : cette vue sera fixée en haut de l’écran lors du scroll

¨   parallax : la vue disparaitra avec un effet parallax, donc avec une vitesse différente du scroll actuel

¨   en l’absence d’un collapseMode, la vue disparait totalement de l’écran lors du scroll, comme vous en avez l’habitude

La référence

242

¨ 

¨  Utiliser les icones dans les ressources

¨ 

Annexes

UX

243

Excellent retour d’expérience sur le montage d’une équipe de User eXpérience

Des tutos

Des trucs bien sympa

Quelques outils pratiques

¨ Affichage du mobile sur un ordi:

¨ Emulateur rapide:

244

¨ Plateforme Cloud mobile pour les notifications & Co :

¨ ¨ Azure de Microsoft ¨ Outils de resize :

?raw=true



15