Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
Android Studio
Préférences-Listes Persistance de l’activité
Les préférences – classe PreferenceFragment
Sur Android cette vue ou activité est activée lorsque l’application est active, en cliquant sur le bouton « settings ».
La vue de « préférences » permet de configurer les paramètres d’application et de les rendre persistants.
Elle permet de définir une valeur par défaut, attribuée pour le premier démarrage de l’application.
Les activités de préférences doivent également être définies en XML, dans un dossier res/xml. Elles n’utilisent pas les contrôles Widgets des activités « classiques » de l’application, mais un jeu de balises spécifiques.
Malheureusement, dans la version actuelle des outils Eclipse-Android ou Android Studio, il n’y a pas de rendu visuel possible des activités de préférences.
La vue de « Préférences »
Le bouton « Préférences »
Il est ici…
Les balises des contrôles “Préférences”…
Les contrôles utilisables sont :
PreferenceScreen : élément racine obligatoire de la hiérarchie de l’activité des préférences,
CheckBoxPreference : contrôle case à cocher,
DialogPreference : contrôle du type boîte de dialogue,
EditTextPreference : contrôle du type zone d’édition texte,
MultiSelectListPreference : liste de sélection multiple,
PreferenceCategory : pour associer des contrôles dans un groupe avec un titre,
RingTonePreference : contrôle choix d’une sonnerie,
SwitchPreference : contrôle commutateur.
userpreferences.xml <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="@string/pref_cat_serveursql"> <EditTextPreference android:key="PREFKEY_IPSERVEUR" android:title="Serveur SQL Express" android:summary="Entrer l'adresse ou le nom d'hôte" android:defaultValue="172.19.32.50" /> <EditTextPreference android:key="PREFKEY_PORTSERVEUR" android:title="Serveur SQL Express" android:summary="Entrer le port d'écoute du serveur" android:defaultValue="1433" /> </PreferenceCategory> <PreferenceCategory android:title="@string/pref_cat_comptesql"> <EditTextPreference android:key="PREFKEY_USERNAME" android:title="Nom d'utilisateur" android:summary="Entrer nom du compte autorisé" android:defaultValue="sa" /> <EditTextPreference android:key="PREFKEY_PASSWORD" android:title="Mot de passe" android:summary="Entrer le mot de passe de connexion" android:defaultValue="" android:inputType="textPassword"/> </PreferenceCategory> </PreferenceScreen>
Le rendu
de l’exemple :
Le rendu
de l’exemple :
La classe PreferenceFragment
La classe java en charge du chargement des préférences définies dans un fichier XML doit hériter de la classe PreferenceFragment.
Elle doit surcharger la méthode onCreate(), celle-ci devant construire les préférences à l’aide du fichier de ressources XML.
On peut également y définir les clés qui serviront à gérer la persistance des valeurs associées aux préférences (mécanisme clé/valeur).
La classe PreferenceFragment hérite elle-même de la classe de base Fragment. Mais alors, c’est quoi un fragment ?
Le fragment est un élément intermédiaire entre l’activité et la vue,
un fragment consiste à définir un « morceau d‘activité » que l’on peut attacher où l’on veut sur des activités, pour recouvrir tout ou partie de la vue…
Le fragment charge sa description XML, et une activité charge le fragment.
La classe PreferenceFragment : Exemple
public class PreferencesFragments extends PreferenceFragment { // Ctes string, clés de sauvegarde des préférences public static final String PREFKEY_IPSERVEUR ="PREFKEY_IPSERVEUR"; public static final String PREFKEY_PORTSERVEUR="PREFKEY_PORTSERVEUR"; public static final String PREFKEY_USERNAME ="PREFKEY_USERNAME"; public static final String PREFKEY_PASSWORD ="PREFKEY_PASSWORD"; @Override public void onCreate(Bundle b) { super.onCreate(b); addPreferencesFromResource(R.xml.userpreferences); } }
La classe Activité, qui charge le fragment
public class SetPreferencesFragmentActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager fm = getFragmentManager(); fm.beginTransaction() .replace(android.R.id.content, new PreferencesFragments()).commit(); } }
Pour gérer les fragments
Début d’une transaction, remplacement d’une activité après sauvegarde
de l’état courant
Remplacement activité courante par le fragment
Lorsque l’état courant est sauvegardé
Elément racine de l’activité
courante
La barre de menu, le bouton préférences Les choses à faire :
créer un menu associé au clic sur le bouton « settings » (on peut avoir
plusieurs vues de préférences), méthode Activity.onCreateOptionsMenu(),
capturer l’événement clic sur la (ou les) rubrique(s) du menu, et faire en
sorte que l’activité « Préférences » associée soit chargée avec attente de
résultat, méthode Activity.onOptionsItemSelected(),
capturer l’événement « retour résultat » de l’activité « Préférences » afin de
mettre à jour les paramètres de l’application, Activity.onActivityResult().
Exemple, création du menu
<string name="menu_preferences">Préférences</string>
Le texte du menu
Identificateur du
menu
Groupe de menu
0 si pas de groupe
static final private int MENU_PREFERENCES = Menu.FIRST; @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, MENU_PREFERENCES, Menu.NONE, R.string.menu_preferences); return true; }
strings.xml
MainActivity.java
Identificateur
de menu Position dans
le groupe
Le texte du menu
static final private int CODE_REQUETE_PREFERENCES = 1; @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); switch (item.getItemId()) { case (MENU_PREFERENCES): { Class c = SetPreferencesFragmentActivity.class; Intent i = new Intent(this, c); startActivityForResult(i, CODE_REQUETE_PREFERENCES); return true; } } return false; }
Exemple, charger l’activité « Préférences »
L’identificateur
du menu
MainActivity.java
Chargement
de l’activité
Préférences
Le code requête
pour tester la
réponse
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CODE_REQUETE_PREFERENCES) this.updateAttributsFromPreferences(); }
Exemple, capturer le retour résultat
Le code requête
attendu
MainActivity.java
On suppose que cette méthode « maison » fait bien les
choses et récupère les paramètres de configuration
pour les recopier dans les attributs ad-hoc,
pour l’instant…
Récupérer les paramètres de configuration
Android met à notre disposition la classe SharedPreferences pour gérer
la persistance des paramètres de configuration.
Son usage n’est pas limité à ceux-ci, on peut aussi l’utiliser pour les
données de l’application.
Les données sont sauvegardées selon le mécanisme clé/valeur.
Les données des contrôles « Préférences » sont automatiquement
sauvegardés dans un fichier du bundle de l’application, avec les clés
spécifiées dans la définition du contrôle, balise android:key.
PreferencesFragments.java userpreferences.xml
<EditTextPreference android:key="PREFKEY_IPSERVEUR" android:title="Serveur SQL Express" android:summary="Entrer l'adresse…" android:defaultValue="172.19.32.50" />
public class PreferencesFragments extends PreferenceFragment { // Clés de sauvegarde des préférences public static final String PREFKEY_IPSERVEUR ="PREFKEY_IPSERVEUR";
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); String ip = prefs.getString( PreferencesFragments.PREFKEY_IPSERVEUR, "172.19.32.50");
RAPPELS
Pour lire les données persistantes
Une petite remarque…
Il va de soit que les valeurs des attributs XML sont, dans cet exemple,
directement affectées par souci de facilité de lecture…
En pratique il est préférable de faire appel au fichier strings.xml.
</resources>
<!-- données de configuration persistantes --> <string name="title_ipServeur">Serveur SQL Express</string> <string name="summary_ipServeur">Entrer l\'adresse ou …</string> </resources>
strings.xml
userpreferences.xml
<EditTextPreference android:key="PREFKEY_IPSERVEUR" android:title="@string/title_ipServeur" android:summary="@string/summary_ipServeur" android:defaultValue="172.19.32.50" />
Classe ClientSQLmetier
Le constructeur
public ClientSQLmetier(String serveur, String port, String bdd, String user, String mdp, int timeout) throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException { this.setServeurBDD(serveur); this.setNomBDD(bdd); this.setUserBDD(user); this.setMdpBDD(mdp); this.setPortBDD(port); String to = String.valueOf(timeout); setConnexionStringBDD("jdbc:jtds:sqlserver://" +getServeurBDD().toString()+":"+port.toString() +"/"+bdd+";encrypt=false;instance=SQLEXPRESS;loginTimeout="+to +";socketTimeout="+to+";"); // Chargement du driver Class.forName("net.sourceforge.jtds.jdbc.Driver").newInstance(); DriverManager.setLoginTimeout(timeout); }
La méthode addNewFournisseur public int addNewFournisseur(int nf,String nom,String statut, String ville) { int result = -1; if( conn == null ) { try { conn = DriverManager.getConnection(this.connexionStringBDD,
this.userBDD, this.mdpBDD); } catch (SQLException e) {result = -1;}
} Log.i(TAG,"open BDD"); Statement stmt; try { stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE); PreparedStatement prepstmt = conn.prepareStatement("INSERT INTO F (NF, nomF, statut, ville) VALUES (?, ?, ?, ?)"); prepstmt.setInt(1, nf); prepstmt.setString(2, nom); prepstmt.setString(3, statut); prepstmt.setString(4, ville); result = prepstmt.executeUpdate(); if(prepstmt != null) prepstmt.close(); if(stmt != null) stmt.close(); } catch (SQLException e) { result = -1;} return result; }
La méthode getTableFournisseurs
public ResultSet getTableFournisseurs() throws SQLException { if( conn == null ) conn =DriverManager.getConnection(this.connexionStringBDD, this.userBDD, this.mdpBDD); Log.i(TAG,"open BDD"); Statement stmt = conn.createStatement(); ResultSet result = stmt.executeQuery("select * from F"); return result; }
La méthode deleteFournisseur public int deleteFournisseur(int NF) { int result = -1; if( conn == null ) { try { conn =DriverManager.getConnection(this.connexionStringBDD, this.userBDD, this.mdpBDD); } catch (SQLException e) {result = -1;} } Log.i(TAG,"open BDD"); Statement stmt; try { stmt= conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE); PreparedStatement prepstmt =conn.prepareStatement( "DELETE FROM F WHERE NF = ?"); prepstmt.setInt(1, NF); result = prepstmt.executeUpdate(); if(prepstmt != null) prepstmt.close(); if(stmt != null) stmt.close(); } catch (SQLException e) {result = -1;} return result; }
La méthode finalize()
public void finalize() { if(conn != null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
Afficher une boîte de message : AlertDialog
Le « Toast » permet d’afficher un message temporaire sans interaction
utilisateur. Si l’on désire afficher un message avec une prise en compte
utilisateur il faut utiliser la classe AlertDialog.
Exemple 1 : AlertDialog sans bouton, un clic la fait disparaitre
AlertDialog.Builder alertB = new AlertDialog.Builder(MainActivity.this); alertB.setTitle("Compte ajouté."); alertB.show();
Afficher une boîte de message : AlertDialog
Exemple 2 : AlertDialog avec 2 boutons, OUI et NON
AlertDialog.Builder alertB = new AlertDialog.Builder(MainActivity.this); alertB.setTitle("Suppression :"); alertB.setMessage("Confirmez-vous la suppression ?"); alertB.setPositiveButton("OUI", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Ici on supprime… } }); alertB.setNegativeButton("NON", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Ici on supprime pas... } }); alertB.show();
Activité avec une « ListView »
Comme son nom l'indique, une ListView est un contrôle de type liste
comprenant des items, généralement séparés par un trait.
Le design de l’item est défini par un « layout XML » prédéfini,
simple_list_item_1 par exemple, ou personnalisé.
Si la liste est « plus longue » que la taille attribuée à la ListView, la
ListView devient automatiquement « scrollable ».
Pour afficher une liste d'item dans la liste, il faut faire passer les données
au travers d’un adaptateur de données, ArrayAdapter par exemple.
Il est possible de se créer son propre adaptateur pour un typage de
données métier, celui-ci doit hériter de la classe BaseAdapter.
Item 1
Item 2
Item 3
Item 4
Item 5
ListView
ArrayAdapter
Les donnéesTableau d’objets
Objet 1
Objet 1
Objet 1
Objet 1
Objet 1
Adaptateur des données
Layout(design de l’item)
Exemple : ListView « de base » pour afficher un tableau d’objets « String ».
<ListView android:layout_height="match_parent" android:layout_width="match_parent" android:layout_marginLeft="10sp" android:id="@+id/ListeF"/>
int layoutID = android.R.layout.simple_list_item_1;
ListView listeView; ArrayList <String> arrayF = new ArrayList<String>();
ArrayAdapter <String> arrayAdapt; ListeView = (ListView)this.findViewById(R.id.ListeF); arrayAdapt = new ArrayAdapter<String>(this,layoutID,arrayF); this.listeView.setAdapter(arrayAdapt);
activity_main.xml
MainActivity.java
Le contrôle ListView
et son identificateur
Le layout de l’item
« simple TextView » qui sera
initialisé avec objet.toString()
L’objet Widget liste
Les objets
L’adaptateur
Les données (objets) Le layout Le contexte
Exemple :
Si les données viennent à changer, il faut le signaler à l’adaptateur
« méthode notifyDatasetChanged() » afin qu’il mette à jour l’affichage .
Item perso et donc Adaptateur perso… Tout comme moi vous avez trouvé l’item intégré bien fade.
Nous allons donc en construire une version perso…
LinearLayout horizontal
ImageView
LinearLayout verticalLinearLayout horizontal
LinearLayout horizontal
TextView
« imgUsine »TextView
TextView
TextView
« nf » « nomF »
« statut » « ville »
Item_perso.xml
Ouah !!
Quel rendu …
La structure Fournisseur, données de l’item perso… Fournisseur.java
public class Fournisseur { public String NF; public String nomF; public String statut; public String ville; public Fournisseur(String NF,String nomF,String statut,String ville) { this.NF = NF; this.nomF = nomF; this.statut = statut; this.ville =ville; } @Override public String toString() { return (this.NF+ "\t : "+this.nomF+ "\t : "+this.statut+ "\t : "+this.ville); } }
Le fichier item_perso.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageView android:id="@+id/imgUsine" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:padding="10dp" android:contentDescription="@string/imageDescription" /> <LinearLayout android:orientation="vertical" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="0dp" android:layout_weight="1" > <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="10dp" >
<TextView android:id="@+id/nf" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/nomF" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" android:layout_marginLeft="30dp" android:textColor="@color/nomF_color" /> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="50dp" > <TextView android:id="@+id/statut" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" /> <TextView android:id="@+id/ville" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" /> </LinearLayout> </LinearLayout> </LinearLayout>
Adaptateur perso, spécialisation de ArrayAdapter
Nécessite la surcharge :
d’un des constructeurs (au moins), dans l’exemple traité le
constructeur suivant sera surchargé :
ArrayAdapter(Context context, int resource, List<T> objects)
de la méthode getView() qui doit retourner la vue à dessiner pour
l’item d’indice reçu en paramètre :
View getView(int position, View convertView, ViewGroup parent
Le contexte courant L’identificateur de la
ressource layout
La liste des objets à
représenter dans la
ListView
L’indice de l’objet
courant dans la
liste
La vue à redessiner
ou null si la vue doit
être créée
L’élément parent de la
vue, s’il existe
La vue pour l’item
d’indice position
La classe ArrayFournisseurAdapteur
public class ArrayFournisseurAdapter extends ArrayAdapter<Fournisseur> { // Déclaration d'une liste d'items private ArrayList<Fournisseur> objets; private int item_id; //Surcharge du constructeur ArrayAdapteur public ArrayFournisseurAdapter(Context context, int textViewResourceId, ArrayList<Fournisseur> objects) { super(context, textViewResourceId, objects); this.objets = objects; this.item_id = textViewResourceId; } }
La classe ArrayFournisseurAdapteur
@Override public View getView(int position, View convertView, ViewGroup parent){ // Vue à mettre à jour View v = convertView; if (v == null) { LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = inflater.inflate(this.item_id, null); } Fournisseur fcourant = objets.get(position); if (fcourant != null) { TextView tv_nf = (TextView) v.findViewById(R.id.nf); TextView tv_nomF = (TextView) v.findViewById(R.id.nomF); TextView tv_statut = (TextView) v.findViewById(R.id.statut); TextView tv_ville = (TextView) v.findViewById(R.id.ville); ImageView icone = (ImageView) v.findViewById(R.id.imgUsine); if (tv_nf != null) tv_nf.setText(fcourant.NF); if (tv_nomF != null) tv_nomF.setText(fcourant.nomF); if (tv_statut != null) tv_statut.setText(fcourant.statut); if (tv_ville != null) tv_ville.setText(fcourant.ville); if (icone != null) icone.setImageResource(R.drawable.usine); } return v; } }
Construction du layout
Paramétrage des contrôles du layout avec les
données de l’objet courant
Lecture de l’objet courant
Objet permettant la création d’une
vue depuis sa description XML
Le chargement de la liste avec item perso
ListView listeView; ArrayList <Fournisseur> arrayF = new ArrayList<Fournisseur>(); ArrayFournisseurAdapter arrayFournisseurAdapt; // Prépa de la listeView this.listeView = (ListView)this.findViewById(R.id.ListeF); // Layout item perso int layoutID = R.layout.item_perso; this.listeView.setAdapter(arrayFournisseurAdapt);
arrayFournisseurAdapt = new ArrayFournisseurAdapter(this,layoutID,arrayF); this.listeView.setAdapter(arrayFournisseurAdapt);
Allure de la liste obtenue
Rotation de la vue - Persistance
La méthode de construction de la vue est appelée à chaque fois que la vue
doit être dessinée, donc également lors des rotations de la vue. protected void onCreate(Bundle savedInstanceState)
Le paramètre savedInstanceState permet de gérer la persistance :
il vaut null au premier chargement de la vue,
Il contient l’état sauvegardé de la vue (si géré) dans un « paquetage »
lorsque la vue est redessinée.
Lorsque la vue est détruite la méthode onSaveInstanceState() est appelée
et permet de sauvegarder les attributs à restaurer pour gérer la persistance. protected void onSaveInstanceState(Bundle outState)
La vue pourra alors être restaurée au moment de l’appel :
soit de onCreate(Bundle savedInstanceState), soit de onRestaureInstanceState(Bundle savedInstanceState).
Les objets sont sauvegardés/restaurés selon le principe de clé/valeur à
l’aide des méthodes Activity.putSerializable() et Activity.getSerializable().
Persistance de la vue – Exemple :
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable(MainActivity.TABLE_F_KEY, this.arrayF); }
static final private String TABLE_F_KEY = "Table_F"; ArrayList <Fournisseur> arrayF = new ArrayList<Fournisseur>();
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.listeView = (ListView)this.findViewById(R.id.ListeF); int layoutID = R.layout.item_perso; if(savedInstanceState!=null) { this.arrayF = (ArrayList<Fournisseur>) savedInstanceState.getSerializable(MainActivity.TABLE_F_KEY); } … }
Persistance…
Petite remarque…
Vous avez bien sûr remarqué que dans putSerializable() et
getSerializable() il y a… serializable !!
En effet, cela impose que les objets persistants implémentent l’interface
Serializable.
Pas de panique ! Cette interface n’impose qu’une chose : que l’on ajoute
un attribut privé statique final de type long nommé serialVersionID
initialisé explicitement et que sa valeur soit incrémentée à chaque
évolution de la classe.
Exemple :
public class Fournisseur implements Serializable { // Contrainte de serialization private static final long serialVersionUID = 12L; … }