{"id":1073,"date":"2020-01-06T00:17:25","date_gmt":"2020-01-05T23:17:25","guid":{"rendered":"http:\/\/chezdom.net\/etu\/?page_id=1073"},"modified":"2020-01-06T00:18:52","modified_gmt":"2020-01-05T23:18:52","slug":"tp4-2019-liste-et-base-de-donnees","status":"publish","type":"page","link":"https:\/\/chezdom.net\/etu\/tp4-2019-liste-et-base-de-donnees\/","title":{"rendered":"TP4 (2019) &#8211; Liste et base de donn\u00e9es"},"content":{"rendered":"<p>L&rsquo;objet de ce TP est de d\u00e9velopper une application pour saisir des mesures (de poids par exemple, mais vous pouvez adapter \u00e0 vos pr\u00e9f\u00e9rences : temp\u00e9rature ext\u00e9rieure, mm de pr\u00e9cipitations, radioactivit\u00e9&#8230;). Pour cela nous allons introduire l&rsquo;\u00e9l\u00e9ment <strong>ListView<\/strong> et utiliser une base de donn\u00e9es.<\/p>\n<p>NB: R\u00e9cemment (2017) le container <strong>ListView<\/strong> a \u00e9t\u00e9 remplac\u00e9 par <strong>RecyclerView<\/strong>. Mais ListView reste pr\u00e9sent et plus facile \u00e0 comprendre pour d\u00e9buter.<\/p>\n<h2>Pr\u00e9liminaire<\/h2>\n<p>(rien de nouveau par rapport aux pr\u00e9c\u00e9dents TPs, mais on en a besoin !)<\/p>\n<p>Cr\u00e9ez une nouvelle application avec 2 activit\u00e9s. La premi\u00e8re recevra la liste des mesures, la seconde servira \u00e0 ajouter une mesure de poids :<\/p>\n<ol>\n<li>Dans la premi\u00e8re activit\u00e9, placez 2 boutons : Ajouter et Quitter (on veillera \u00e0 utiliser comme layout principal un layout lin\u00e9aire vertical, et on placera les 2 boutons dans un layout lin\u00e9aire horizontal en haut.<\/li>\n<li>Dans la seconde activit\u00e9 (elle aussi en layout lin\u00e9aire vertical), placez 2 \u00e9l\u00e9ments : un champs de saisie (d\u00e9cimal) et un bouton \u00ab\u00a0Ajouter\u00a0\u00bb.<\/li>\n<li>Dans le code de l&rsquo;activit\u00e9 principale, impl\u00e9mentez les actions n\u00e9cessaires aux 2 boutons : le bouton ajouter va lancer la seconde activit\u00e9, et le bouton quitter, comme son nom l&rsquo;indique&#8230;<\/li>\n<li>Dans l&rsquo;activit\u00e9 principale, ajouter un champ de texte temporaire : on pourra l&rsquo;enlever dans une version suivante, ou bien le recycler pour afficher autre chose. Pour l&rsquo;instant il va seulement nous servir \u00e0 v\u00e9rifier que le retour de la premi\u00e8re activit\u00e9 se passe correctement &#8211; il n&rsquo;est donc pas n\u00e9cessaire de passer du temps \u00e0 sa pr\u00e9sentation. On va juste l&rsquo;ajouter dans le layout lin\u00e9aire principal.<\/li>\n<li>Compl\u00e9tez le code des 2 activit\u00e9s pour que la nouvelle saisie soit affich\u00e9e dans le champ de texte temporaire. Pour l&rsquo;instant on va transmettre la saisie sous la forme de la cha\u00eene de caract\u00e8res saisi (type String).<\/li>\n<li>Pour terminer on va, dans l&rsquo;activit\u00e9 principale, convertir la valeur retourn\u00e9e en double. Pour le d\u00e9bogage, on pourra par exemple afficher cette valeur multipli\u00e9e par 2 (afin d&rsquo;\u00eatre bien s\u00fbr que la conversion a \u00e9t\u00e9 correcte).<\/li>\n<\/ol>\n<p>2 remarques pour terminer ce pr\u00e9liminaire :<\/p>\n<ul>\n<li>Si vous avez bien int\u00e9gr\u00e9 les pr\u00e9c\u00e9dents cours, cette \u00e9tape pr\u00e9liminaire ne doit pas vous prendre plus de 20 minutes, 1\/2h si vous tapez avec 2 doigts&#8230;<\/li>\n<li>Si vous n&rsquo;arrivez pas \u00e0 faire la conversion en double, vous pouvez garder la valeur sous forme de cha\u00eene de caract\u00e8res pour les \u00e9tapes suivantes.<\/li>\n<\/ul>\n<hr \/>\n<h2>Premi\u00e8re \u00e9tape<\/h2>\n<p>Nous allons maintenant conserver toutes les saisies dans une liste. Pour cela on va ajouter sur l&rsquo;\u00e9cran principal un widget de type ListView. Vous pouvez conserver la premi\u00e8re version en clonant votre projet, c&rsquo;est \u00e0 dire en en cr\u00e9ant un second (par exemple en weightmonitorV2) \u00e0 partir d&rsquo;une copie du premier. Voir le tuto <a href=\"http:\/\/chezdom.net\/etu\/dupliquer-un-projet-android\/\">Dupliquer ou renommer un projet android<\/a><\/p>\n<ol>\n<li>Ouvrez le layout de l&rsquo;activit\u00e9 principale. Ajoutez un container de type <strong>ListView<\/strong> (vous le trouverez dans la cat\u00e9gorie <em>Legacy<\/em>) dessous du champs de texte temporaire.<\/li>\n<li>V\u00e9rifiez l&rsquo;id de ce ListView. Il doit \u00eatre \u00ab\u00a0<strong>@android:id\/list<\/strong>\u00a0\u00bb (mettez le si ce n&rsquo;a pas \u00e9t\u00e9 fait automatiquement).<\/li>\n<\/ol>\n<p>On va maintenant passez \u00e0 l&rsquo;impl\u00e9mentation. Dans le fichier MainActivity.java. L&rsquo;id\u00e9e est de r\u00e9cup\u00e9rer une r\u00e9f\u00e9rence \u00e0 cette liste et de l&rsquo;associer \u00e0 un tableau de donn\u00e9es (ici ce sera une liste de cha\u00eenes de caract\u00e8res).<\/p>\n<p>Mais le fonctionnement des containers de type ListView est un peu particulier et il va falloir modifier le type de la classe principale. Au lieu d&rsquo;Activity (on de AppCompatActivity), on va utiliser <strong>ListActivity<\/strong>. Cette classe comprend entre autres une m\u00e9thode <strong>GetListView<\/strong>, qu&rsquo;on utilisera pour r\u00e9cup\u00e9rer une r\u00e9f\u00e9rence au container de type ListView (\u00e0 la place de FindViewById qu&rsquo;on a l&rsquo;habitude d&rsquo;utiliser pour acc\u00e9der \u00e0 d&rsquo;autres objets de l&rsquo;interface).<\/p>\n<ol>\n<li> Donc on va d&rsquo;abord r\u00e9cup\u00e9rer la r\u00e9f\u00e9rence au ListView (dans <em>OnCreate<\/em>) :\n<pre class=\"brush: java; gutter: false\">\nListView listView=this.getListView();\n<\/pre>\n<\/li>\n<li>Puis, on va cr\u00e9er une liste de cha\u00eenes de caract\u00e8res. Comme on aura besoin d&rsquo;acc\u00e9der \u00e0 cette liste depuis d&rsquo;autres m\u00e9thodes on va en faire un attribut de la classe:\n<pre class=\"brush: java; gutter: false\">\nprivate ArrayList&lt;String&gt; maListe;\n<\/pre>\n<p>et dans *OnCreate* :<\/p>\n<pre class=\"brush: java; gutter: false\">\nmaListe=new ArrayList&lt;String&gt;();\n<\/pre>\n<\/li>\n<li>Enfin on va cr\u00e9er un objet \u00ab\u00a0adapter\u00a0\u00bb qui va servir \u00e0 adapter notre liste de cha\u00eenes de caract\u00e8res avec le container de type ListView. Dans un premier temps on va utiliser un <strong>ArrayAdapter<\/strong> pr\u00e9fabriqu\u00e9, par la suite on verra qu&rsquo;on peut en impl\u00e9menter un. Comme tout \u00e0 l&rsquo;heure on aura besoin de cet adapter plus tard, il faut donc en faire un attribut :\n<pre class=\"brush: java; gutter: false\">\nprivate ArrayAdapter&lt;String&gt; adapter;\n<\/pre>\n<p>et on le cr\u00e9e dans <em>OnCreate<\/em> avant de l&rsquo;associer au listView (seconde ligne ci-dessous) :<\/p>\n<pre class=\"brush: java; gutter: false\">\nadapter=new ArrayAdapter&lt;String&gt;(this, android.R.layout.simple_list_item_1, maListe);\nlistView.setAdapter(adapter);\n<\/pre>\n<\/li>\n<li>Finalement dans la m\u00e9thode <em>onActivityResult<\/em>, on va ajouter la valeur qu&rsquo;on vient de lire (o\u00f9 wval est la valeur lue, de type double, adaptez si vous avez gard\u00e9 cette valeur sous la forme d&rsquo;une cha\u00eene). Il faut ensuite informer l&rsquo;adapter que la valeur a \u00e9t\u00e9 modifi\u00e9e, pour que l&rsquo;affichage soit mis \u00e0 jour (second ligne ci-dessous).\n<pre class=\"brush: java; gutter: false\">\nmaListe.add(String.format(&quot;Val : %f&quot;,wval));\nadapter.notifyDataSetChanged();\n<\/pre>\n<\/li>\n<\/ol>\n<p>Pour r\u00e9sumer ce qui doit \u00eatre chang\u00e9\/ajout\u00e9 par rapport \u00e0 l&rsquo;\u00e9tape pr\u00e9liminaire :<\/p>\n<pre class=\"brush: java; gutter: false\">\npublic class MainActivity extends ListActivity implements View.OnClickListener {\n    private ArrayList&lt;String&gt; maListe;\n    private ArrayAdapter&lt;String&gt; adapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        \/\/ .....\n\n        maListe=new ArrayList&lt;String&gt;();\n        ListView listView=this.getListView();\n        adapter=new ArrayAdapter&lt;String&gt;(this, android.R.layout.simple_list_item_1, maListe);\n        listView.setAdapter(adapter);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode,\n    @Nullable Intent data) {\n        \/\/ ...\n        maListe.add(String.format(&quot;Val : %f&quot;,wval));\n        adapter.notifyDataSetChanged();\n        \/\/ ...\n    }\n<\/pre>\n<p>Ex\u00e9cutez cela dans une machine virtuelle ou sur un t\u00e9l\u00e9phone r\u00e9el, ajoutez suffisamment de lignes pour remplir l&rsquo;\u00e9cran. Vous constaterez que lorsque la capacit\u00e9 de l&rsquo;\u00e9cran est d\u00e9pass\u00e9, une barre de d\u00e9filement appara\u00eet automatiquement.<\/p>\n<hr \/>\n<h2>Seconde \u00e9tape<\/h2>\n<p>Le probl\u00e8me maintenant, c&rsquo;est que lorsqu&rsquo;on quitte l&rsquo;application, on perd nos valeurs. Quelle solution proposez vous ? Les stocker dans la base de donn\u00e9es du t\u00e9l\u00e9phone ? Oui c&rsquo;est cela \ud83d\ude00<\/p>\n<p>Une fois les donn\u00e9es enregistr\u00e9es dans la base de donn\u00e9es on pourra les r\u00e9cup\u00e9rer \u00e0 l&rsquo;ouverture de l&rsquo;application.<\/p>\n<ol>\n<li>En vous aidant du cours <a href=\"http:\/\/chezdom.net\/etu\/wp-content\/uploads\/sites\/3\/2013\/11\/Android-DB.paper_.pdf\">Sauvegarder des donn\u00e9es<\/a>, cr\u00e9ez une classe <strong>Contrat<\/strong> et une classe <strong>DBHelper<\/strong>.<\/li>\n<li>Ensuite dans le <em>onCreate<\/em> de l&rsquo;activit\u00e9 principale, on r\u00e9cup\u00e8re la base grace au DB Helper<\/li>\n<li>puis \u00e0 l&rsquo;endroit o\u00f9 vous avez plac\u00e9 la valeur dans liste, enregistrez la aussi dans la base (cf <a href=\"http:\/\/chezdom.net\/etu\/wp-content\/uploads\/sites\/3\/2013\/11\/Android-DB.paper_.pdf\">cours<\/a>, section 4.2).<\/li>\n<li>Il reste \u00e0 lire les valeurs dans la base au chargement de l&rsquo;application, pour cela voir la section 4.4 du <a href=\"http:\/\/chezdom.net\/etu\/wp-content\/uploads\/sites\/3\/2013\/11\/Android-DB.paper_.pdf\">cours<\/a>, et \u00e0 les ajouter \u00e0 la liste initiale dans <em>onCreate<\/em> donc.<\/li>\n<\/ol>\n<h3>Quelques indications :<\/h3>\n<h4>Classe Contrat<\/h4>\n<pre class=\"brush: java; gutter: false\">\nclass WeightEntriesContract implements BaseColumns {\n    public static final String TABLE_NAME = &quot;weightentries&quot;;\n    public static final String FIELD_ENTRY_ID = &quot;entryid&quot;;\n    public static final String FIELD_DATE = &quot;date&quot;;\n    public static final String FIELD_VALUE = &quot;weigh&quot;;\n\n    private WeightEntriesContract() {}\n}\n<\/pre>\n<h4>Classe DBHelper<\/h4>\n<pre class=\"brush: java; gutter: false\">\nclass WeightMonDBHelper extends SQLiteOpenHelper {\n    public static final int DATABASE_VERSION = 1;\n    public static final String DATABASE_NAME = &quot;MyApp.db&quot;;\n    private static final String TEXT_TYPE = &quot; TEXT&quot;;\n    private static final String DATE_TYPE = &quot; DATETIME&quot;;\n    private static final String DOUBLE_TYPE = &quot; REAL&quot;;\n    private static final String COMMA_SEP = &quot;,&quot;;\n\n    private static final String SQL_CREATE_ENTRIES =\n            &quot;CREATE TABLE &quot; +WeightEntriesContract.TABLE_NAME +&quot; (&quot;\n                    +WeightEntriesContract._ID +&quot; INTEGER PRIMARY KEY,&quot;\n                    +WeightEntriesContract.FIELD_ENTRY_ID +TEXT_TYPE +COMMA_SEP\n                    +WeightEntriesContract.FIELD_DATE +DATE_TYPE +COMMA_SEP\n                    +WeightEntriesContract.FIELD_VALUE +DOUBLE_TYPE\n            +&quot; )&quot;;\n\n    private static final String SQL_DELETE_ENTRIES =\n            &quot;DROP TABLE IF EXISTS &quot; + WeightEntriesContract.TABLE_NAME;\n\n    public WeightMonDBHelper(@Nullable Context context) {\n        super(context, DATABASE_NAME, null, DATABASE_VERSION);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        db.execSQL(SQL_CREATE_ENTRIES);\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int i, int i1) {\n        \/\/ nothing to do so far (it&#039;s v1)\n    }\n}\n<\/pre>\n<h4>Dans l&rsquo;activit\u00e9 principale, dans <em>OnCreate<\/em> :<\/h4>\n<pre class=\"brush: java; gutter: false\">\n   WeightMonDBHelper mDbHelper=new WeightMonDBHelper(this);\n   db = mDbHelper.getWritableDatabase();\n<\/pre>\n<h4>Dans <em>onActivityResult<\/em><\/h4>\n<pre class=\"brush: java; gutter: false\">\n   ContentValues values = new ContentValues();\n   values.put(WeightEntriesContract.FIELD_VALUE, wval);\n   long newRowId = db.insert(WeightEntriesContract.TABLE_NAME, null, values);\n<\/pre>\n<hr \/>\n<h2>\u00c9tapes suivantes :<\/h2>\n<ul>\n<li>Ajouter la date de mesure<\/li>\n<li>Afficher les dates dans la liste<\/li>\n<\/ul>\n<h3>Ajouter la date de mesure<\/h3>\n<p>Pour cette troisi\u00e8me \u00e9tape, il faut impl\u00e9menter les actions suivantes (vous disposez de toutes les infos n\u00e9cessaires pour cela) :<\/p>\n<ul>\n<li>Ajouter un champ \u00e0 la seconde activit\u00e9<\/li>\n<li>Renvoyer la date \u00e0 l&rsquo;activit\u00e9 principale<\/li>\n<li>Sauvegarder la date dans la base<\/li>\n<\/ul>\n<h3>Afficher les dates dans la liste<\/h3>\n<p>Pour la quatri\u00e8me \u00e9tabpe, on va cr\u00e9er une classe adapter sur mesure, en la faisant d\u00e9river de la classe Adapter. Cela nous permettra d&rsquo;utiliser un fichier de layout sp\u00e9cifique pour les lignes de la liste. C&rsquo;est \u00e0 dire un fichier de layout qui contiendra la mise en page d&rsquo;une ligne de la liste, et qui sera donc r\u00e9p\u00e9t\u00e9 avec chacune des entr\u00e9es \u00e0 afficher.<\/p>\n<p><em>to be continued&#8230;<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>L&rsquo;objet de ce TP est de d\u00e9velopper une application pour saisir des mesures (de poids par exemple, mais vous pouvez adapter \u00e0 vos pr\u00e9f\u00e9rences : temp\u00e9rature ext\u00e9rieure, mm de pr\u00e9cipitations, radioactivit\u00e9&#8230;). Pour cela nous allons introduire l&rsquo;\u00e9l\u00e9ment ListView et utiliser<span class=\"ellipsis\">&hellip;<\/span> <span class=\"read-more\"><a href=\"https:\/\/chezdom.net\/etu\/tp4-2019-liste-et-base-de-donnees\/\">Lire la suite &#8250;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"jetpack_post_was_ever_published":false,"footnotes":""},"class_list":["post-1073","page","type-page","status-publish","hentry"],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/P9qu1A-hj","_links":{"self":[{"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/pages\/1073","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/comments?post=1073"}],"version-history":[{"count":26,"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/pages\/1073\/revisions"}],"predecessor-version":[{"id":1099,"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/pages\/1073\/revisions\/1099"}],"wp:attachment":[{"href":"https:\/\/chezdom.net\/etu\/wp-json\/wp\/v2\/media?parent=1073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}