Vous êtes sur la page 1sur 21
Bienvenue ,ceci est la deuxième partie du tutoriel précédant [Tutoriel] :Communication Android avec MySQL [PHP]

Bienvenue ,ceci est la deuxième partie du tutoriel précédant [Tutoriel] :Communication Android avec MySQL [PHP] [ Partie 1/2 ] . Notre objectif est de faire communiquer notre application Android avec une base de données MySQL et faire différents opérations (CRUD). Comme le titre indique il y a un BONUS

opérations (CRUD). Comme le titre indique il y a un BONUS . Voici le résultat final

.

Voici le résultat final de ce tutoriel :

Je sais que vous allez "scroller" jusqu'à la fin pour voir le Bonus , mais , je vais commencer par ce bonus tout d'abord

voir le Bonus , mais , je vais commencer par ce bonus tout d'abord Partie 0

Partie 0 : Bonus : ProgressDialog personnalisé:

CustomProgressDialog Plusieurs entre vous développent des applications android qui nécessitent que l'utilisateur

CustomProgressDialog

Plusieurs entre vous développent des applications android qui nécessitent que l'utilisateur attend un peu du temps afin que le traitement soit terminé . Pour cela on utilise des progressDiaolg pour demander de l'utilisateur de patienter ou l'informer . Je vais vous montrer comment personnaliser un progressDialog moderne . Tout d'abord on crée un nouveau projet Android (vous le savez comme même ) On va créer une classe "CustomProgressDialog"qui s'étend de la classe "Diaolg":et avec un ctrl + shift + o tout va bien :

Diaolg": et avec un ctrl + shift + o tout va bien : publicclassCustomProgressDialogextendsDialog{

publicclassCustomProgressDialogextendsDialog{

privateImageViewiv;

publicCustomProgressDialog(Contextcontext,intresourceIdOfImage){

super(context,R.style.TransparentProgressDialog);

WindowManager.LayoutParamswlmp=getWindow().getAttributes();

wlmp.gravity=Gravity.CENTER_HORIZONTAL;

getWindow().setAttributes(wlmp);

setCancelable(false);

setOnCancelListener(null);

LinearLayoutlayout=newLinearLayout(context);

layout.setOrientation(LinearLayout.VERTICAL);

LinearLayout.LayoutParamsparams=new

LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);

iv=newImageView(context);

iv.setImageResource(resourceIdOfImage);

layout.addView(iv,params);

addContentView(layout,params);

}

@Override publicvoidshow(){ super.show();

RotateAnimationanim=newRotateAnimation(0.0f,360.0f,

Animation.RELATIVE_TO_SELF,.5f,Animation.RELATIVE_TO_SELF,.5f);

anim.setInterpolator(newLinearInterpolator()); anim.setRepeatCount(Animation.INFINITE);

anim.setDuration(3000);

iv.setAnimation(anim);

iv.startAnimation(anim);

}

}

- Il nous reste qu'ajouter la configuration du style dans le fichier"res/values/styles.xml":

<!-- Customdialog--> <stylename="CustomProgressDialog"parent="@android:Theme.Dialog"> <itemname="android:windowFrame">@null</item> <itemname="android:windowBackground">@android:color/transparent</item> <itemname="android:windowIsFloating">true</item> <itemname="android:windowContentOverlay">@null</item> <itemname="android:windowTitleStyle">@null</item> <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> <itemname="android:windowSoftInputMode">stateUnspecified|adjustPan</item> <itemname="android:backgroundDimEnabled">true</item> <itemname="android:background">@android:color/transparent</item> </style>

- Pour tester ce progressDialog , il suffit d'instancier la classe et appeler la méthode "show":

CustomProgressDialog progressDialog=newCustomProgressDialog(this, R.drawable.loading_throbber); progressDialog.setCancelable(true); progressDialog.show(); //progressDialog.dismiss();

- Ajouter cette photo dans le dossier drawable (vous pouvez la changer par un autre modèle) :

Partie 1 : Let's code:

changer par un autre modèle) : Partie 1 : Let's code: Revenons à notre objectif principal

Revenons à notre objectif principal : communiquer notre application Android avec une base de données MySQL et faire différents opérations (CRUD). Comme on vu dans la première partie, notre base (avec un seul table et 4 champs ) est un peu générale , vous pouvez travaillez sur vos base .

4 champs ) est un peu générale , vous pouvez travaillez sur vos base . Voici

Voici le concept général de notre application :

4 champs ) est un peu générale , vous pouvez travaillez sur vos base . Voici

O: Avant de commencer : AsyncTask , HTTP request , parsing ?? :

Il y a des notions qu'on va les utiliser dans le développement de notre application:

1. AsyncTask: vous permet de faire proprement et facilement des opérations en parallèle du thread UI. Cette classe permet d'effectuer des opérations d'arrière-plan et de publier les résultats dans le thread UI sans avoir à manipuler de threads ou de handlers.

privateclassAddDataAsyncTaskextends AsyncTask<Void,Void,Void>{

@Override

protectedvoidonPreExecute(){

//TODOAuto-generatedmethodstub

super.onPreExecute();

//fairedestraitementavantmettredesrequétes:montrerprogressDialogparexmple

}

@Override

protectedVoiddoInBackground(Void

params){

//TODOAuto-generatedmethodstub

//icionfaitlesrequetes,retournerlesreponserettransformerlesdonnéesJSON(Parsing)

returnnull;

}

@Override

protectedvoidonPostExecute(Voidresult){

//TODOAuto-generatedmethodstub

super.onPostExecute(result);

//icionécritlecodeaprésl'exécutiondedoInBackground

//parexmemple:cacheleprogressDialog,afficherlesrésultatsetc

}

}

//icionécritlecodeaprésl'exécutiondedoInBackground //parexmemple:cacheleprogressDialog,afficherlesrésultatsetc } }
Remarques: Éviter les traitements coté U/I dans la méthode doInBackgound ( passage entre activités ,

Remarques:

Éviter les traitements coté U/I dans la méthode doInBackgound ( passage entre activités ,Remarques: cache le progressDialog etc ) Essayer toujours de faire des logs (afficher les résultats dans

cache le progressDialog etc

)

Essayer toujours de faire des logs (afficher les résultats dans le logcat ) pour découvrir( passage entre activités , cache le progressDialog etc ) facilement les erreurs. (même après chaque

facilement les erreurs. (même après chaque ligne Log.i("msg",monValeur))

Il faut bien connaître le retour de vos fonctions PHP (JSON) afin de les récupérer . (JSONObject, JSONArray etc. )(même après chaque ligne Log.i("msg",monValeur)) VERIFIER QUE LA PERMESSION INTERNET EST AJOUTÉE SVP .

VERIFIER QUE LA PERMESSION INTERNET EST AJOUTÉE SVP .afin de les récupérer . (JSONObject, JSONArray etc. ) Essayer d'utiliser l’émulateur Genymotion : Rapide

Essayer d'utiliser l’émulateur Genymotion : Rapide , performant Genymotion : Rapide , performant

Pour faire des requêtes HTTP et retourner la réponse , on va utiliser utiliser une classe développée par Ravi Tamada dans son fameux site des tutoriels AndroidHive :ServiceHandler:elle va nous facilite les choses

fameux site des tutoriels AndroidHive : ServiceHandler: elle va nous facilite les choses http://pastebin.com/4VsfVvCA

1: ActivityMain :Ajouter des données à la base :

Cette activité a pour but ,d'ajouter des données à la base insérées par l’utilisateur. Voici le code xml du layout activity_main : http://pastebin.com/TE7whc3w

xml du layout activity_main : http://pastebin.com/TE7whc3w Avant de passer à coder MainActivity , rappelons qu'on

Avant de passer à coder

: http://pastebin.com/TE7whc3w Avant de passer à coder MainActivity , rappelons qu'on va utiliser le fichier

MainActivity

,rappelons qu'on va utiliser le fichier ajout_bd.php

, rappelons qu'on va utiliser le fichier ajout_bd.php Donc on va transmettre 3 valeurs : col2,

Donc on va transmettre 3 valeurs : col2, col3 et col4 ==> HTTP POST request et on a comme résultat : un seul objet (JSONObject) qui contient un entier et une chaîne de caractères (String)

un entier et une chaîne de caractères ( String ) Let's code ! Dans la classe

Let's code !

Dans la classe MainActivity , on va déclarer des variables globales (avant on Create()) :

CustomProgressDialog progressDialog; Buttonajout,annuler;

EditTextcol2Valeur,col3Valeur,col4Valeur;

StringurlAdd="http://address_IP_du_PC/enis_android_club/ajout_bd.php";

AddDataAsyncTaskAddData;//instancedenotreasyncTask

Stringmessage;

intsuccess;

-Dans la méthode onCreate,on définit les variables (les boutons etc ) , les listeners des boutons etc. :

@Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressDialog=newCustomProgressDialog(this, R.drawable.loading_throbber); progressDialog.setCancelable(true); ajout=(Button)findViewById(R.id.ajout); annuler=(Button)findViewById(R.id.annuler);

col2Valeur=(EditText)findViewById(R.id.col2);

col3Valeur=(EditText)findViewById(R.id.col3);

col4Valeur=(EditText)findViewById(R.id.col4);

AddData=newAddDataAsyncTask(); ajout.setOnClickListener(newOnClickListener(){ @Override

publicvoidonClick(Viewarg0){

AddData.execute();// exécuter l'asyncTask

}

});

}

- Maintenant on va définir notre asyncTask pas à pas :Dans la même classe Main , on crée une classe privée s'étend de AsyncTask nommée :AddDataAsyncTask

privateclassAddDataAsyncTaskextends AsyncTask<Void,Void,Void>{ @Override protectedvoidonPreExecute(){ Log.i("add","onPreExecute"); super.onPreExecute();

//icionvaaffichernotreprogressDialog

}

@Override

protectedVoiddoInBackground(Void

params){

Log.i("add","startdoInBackground");

/*icionvafairenotrerequêtedetypePOST

puisqu'onadesdonnéesàenvoyer

récupérerlaréponseetextrairelesdonnées

commeonditprécédemment,leréponsecontientun

seulJSONObjectavecunentieretString*/

Log.i("add","enddoInBackground");

returnnull;

}

@Override

protectedvoidonPostExecute(Voidresult){

Log.i("add","onPostExecute");

super.onPostExecute(result);

/*icionvacacherleprogressDialog

etafficherunToastselonlerésultat*/

}

}

- Donc on obtient : (http://pastebin.com/Apv7KYMi : code total)

privateclassAddDataAsyncTaskextends AsyncTask<Void,Void,Void>{ @Override protectedvoidonPreExecute(){ Log.i("add","onPreExecute"); super.onPreExecute(); progressDialog.show();

}

@Override

protectedVoiddoInBackground(Void

params){

Log.i("add","startdoInBackground");

//Creatingservicehandlerclassinstance

ServiceHandlersh=newServiceHandler();

List<NameValuePair>nameValuePair=newArrayList<NameValuePair>(1);

nameValuePair.add(new

BasicNameValuePair("col2",col2Valeur.getText().toString()));

nameValuePair.add(new

BasicNameValuePair("col3",col3Valeur.getText().toString()));

nameValuePair.add(new

BasicNameValuePair("col4",col4Valeur.getText().toString()));

//Makingarequesttourlandgettingresponse

StringjsonStr=sh.makeServiceCall(urlAdd,

ServiceHandler.POST,nameValuePair);//type=POST

Log.d("Response:",jsonStr);

if(jsonStr!=null){

try{

JSONObjectjsonObj=newJSONObject(jsonStr);

success=jsonObj.getInt("success");

message=jsonObj.getString("message");

Log.i("suucess",String.valueOf(success));

Log.i("message",message);

}catch(JSONExceptione){

e.printStackTrace();

}

}

Log.i("add","enddoInBackground");

returnnull;

}

@Override

protectedvoidonPostExecute(Voidresult){

Log.i("add","onPostExecute");

super.onPostExecute(result);

if(progressDialog.isShowing())

{progressDialog.dismiss();

}

if(success==1)

{Toast.makeText(getApplicationContext(),"Good"+message,

Toast.LENGTH_LONG).show();

}

else

{Toast.makeText(getApplicationContext(),"Erreur"+message,

Toast.LENGTH_LONG).show();

}

}

}

- & ainsi le résultat:

Toast.LENGTH_LONG).show(); } } } - & ainsi le résultat: logcat

logcat

Remarque: Si vous cliquez autre fois sur le bouton ajouter , votre application va se

Remarque:

Si vous cliquez autre fois sur le bouton ajouter , votre application va se fermer (crasher) et l'erreur c'est que AddData est nulle .La solution est de changer la méthode onClick comme suit :Remarque: publicvoidonClick(Viewarg0){ AddData=newAddDataAsyncTask(); AddData.execute(); } 2: ListDataActivity :Afficher

publicvoidonClick(Viewarg0){

AddData=newAddDataAsyncTask();

AddData.execute();

}

2: ListDataActivity :Afficher les données de la base :

- On ajoute une nouvelle activité à la projet ListDataActivity dont on va lister les données insérées dans notre base en appelant le fichier affichage_bd.php. Rappelons qu'on a pas des données à envoyer ==>HTTP GET & pour la réponse , il diffère selon la valeur de success:

Si success=0: success=0:

des données à envoyer ==> HTTP GET & pour la réponse , il diffère selon la

Donc on a dans ce cas un seul JSONObject qui contient un entier et un string.

Si success=1 : success=1:

qui contient un entier et un string. Si success=1 : Donc on a dans ce cas

Donc on a dans ce cas un seul JSONObject qui contient un entier (success=1) ,et JSONArray (tableau) dont les indices sont les col1, col2, col3 et col4

- Ainsi , dans l'asyncTask (doInBackground) , on va travailler sur la valeur success, on le récupere tout d’abord , & selon son valeur on décide de récupérer le reste des données

- On va procéder comme la première activité.

- Notre layoutcomporte qu'une listView:http://pastebin.com/u8SbFfT5

- Pour faciliter le travail , notre listView sera simple non personnalisé (les données d'une ligne de la base seront affichées dans la même ligne ) . Si vous voulez personnaliser votre liste , jetez un œil sur ce tutoriel

ersonnaliser votre liste , j etez un œil sur ce tutoriel : [ Tutoriel] : ListView

Variables globales : :

CustomProgressDialog progressDialog;

StringurlGet="http://192.168.1.*/enis_android_club/affichage_bd.php";

//adresseIPduPC

GetDataAsyncTaskgetData;

Stringmessage;

intsuccess;

ListViewlv;

List<String>myListofData;

ArrayAdapterarrayadp;

OnCreate : :

@Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_list_data); progressDialog=newCustomProgressDialog(this, R.drawable.loading_throbber); progressDialog.setCancelable(true);

lv=(ListView)findViewById(R.id.listView1);

myListofData=newArrayList<String>();

getData=newGetDataAsyncTask();

getData.execute();

}

Notre asyncTask : GetDataAsynck (la même façon que la précédente): :

privateclassGetDataAsyncTaskextends AsyncTask<Void,Void,Void>{ @Override protectedvoidonPreExecute(){ Log.i("add","onPreExecute"); super.onPreExecute(); progressDialog.show();

}

@Override

protectedVoiddoInBackground(Void

params){

Log.i("add","startdoInBackground");

ServiceHandlersh=newServiceHandler();

//Makingarequesttourlandgettingresponse

StringjsonStr=sh.makeServiceCall(urlGet,ServiceHandler.GET);

Log.d("Response:",jsonStr);

if(jsonStr!=null){

try{

JSONObjectjsonObj=newJSONObject(jsonStr); //returnvalueofsuccess success=jsonObj.getInt("success"); Log.i("success",String.valueOf(success));

if(success==0)

{

 

//success=0==>thereisastring=message

message=jsonObj.getString("message");

Log.i("message",message);

}

elseif(success==1)

 

{

 

//success=1==>thereisanarrayofdata=valeurs

JSONArraydataValues=jsonObj.getJSONArray("valeurs"); //loopeachrowinthearray

for(intj=0;j<dataValues.length();j++)

{

JSONObjectvalues=dataValues.getJSONObject(j);

//returnvaluesofcol1invalCol1

StringvalCol1=values.getString("col1");

//returnvaluesofcol2invalCol2

StringvalCol2=values.getString("col2");

StringvalCol3=values.getString("col3");

StringvalCol4=values.getString("col4");

//addastringwitchcontainsallofdatagettedfromthe

response

 

myListofData.add(valCol1+"-"+valCol2+"-"+valCol3+"-

"+valCol4);

 

Log.i("Row"+(j+1),valCol1+"-"+valCol2+"-"+valCol3+"-

"+valCol4);

 

}

 

}

}catch(JSONExceptione){

e.printStackTrace();

}

}else{

Log.e("ServiceHandler","Couldn'tgetanydatafromtheurl");

}

Log.i("add","enddoInBackground");

returnnull;

}

@Override

protectedvoidonPostExecute(Voidresult){

Log.i("add","onPostExecute");

super.onPostExecute(result);

if(progressDialog.isShowing())

{

progressDialog.dismiss();

}

if(success==1)

{

Toast.makeText(getApplicationContext(),"Bienrécues", Toast.LENGTH_LONG).show(); //showthelistviewcontainsthedata arrayadp=newArrayAdapter(getApplicationContext(),

android.R.layout.simple_list_item_1,

myListofData);

lv.setAdapter(arrayadp);

}

else

{

Toast.makeText(getApplicationContext(),"Erreur",

Toast.LENGTH_LONG).show();

}

}

}

Remarques:

J'ai ajouté un autre bouton dans la 1er activité qui permet d'afficher la 2eme (ListDatActivity)N'oublier pas de vérifier que la deuxième activité est ajoutée dans le manifest.xml Vous pouvez

N'oublier pas de vérifier que la deuxième activité est ajoutée dans le manifest.xmlqui permet d'afficher la 2eme (ListDatActivity) Vous pouvez également changer l'adaptateur de la

Vous pouvez également changer l'adaptateur de la listeView.que la deuxième activité est ajoutée dans le manifest.xml L'asynckTask est exécuté dés que l'activité est

L'asynckTask est exécuté dés que l'activité est crée.pouvez également changer l'adaptateur de la listeView. Il faut être attentif à la format JSON du

Il faut être attentif à la format JSON du réponse: JSONObject , JSONArray (array de données)est exécuté dés que l'activité est crée. exemple d'affichage d'une ligne de la liste Voici le

JSON du réponse: JSONObject , JSONArray (array de données) exemple d'affichage d'une ligne de la liste

exemple d'affichage d'une ligne de la liste

Voici le code total de la deuxième activité http://pastebin.com/qUEeT8AJ

3: EditSuppActivity :Editer / Supprimer une donnée de la base :

Pour le moment , nous avons développés deux activités qui contiennent deux asyncktask (GET & POST ) avec différents réponses. GET & POST) avec différents réponses.

Cette activité (EditSuppActivity ) à pour but de modifier ou supprimer une donnée choisit par utilisateur en EditSuppActivity) à pour but de modifier ou supprimer une donnée choisit par utilisateur en cliquant sur une ligne de listeView.

Pour moi , j'ai simplifié la liste en mettant tout les données dans la même ligne , donc en cliquant sur une ligne , je vais extraire les données chacune dans une variable et les envoient vers cette activité en utilisant intent intent

On modifie l'activité ListDataActivity en ajoutant dans la méthode onCreate: ListDataActivityen ajoutant dans la méthodeonCreate:

lv.setOnItemClickListener(newOnItemClickListener(){

publicvoidonItemClick(AdapterView<?>arg0,Viewarg1,intarg2,long

arg3)

{

//s=valueofseletedrow

Strings=(String)(lv.getItemAtPosition(arg2));

//oneachrow,Ihavesaveallofdatasepartedby'-':col1-

col2-col3-col4

String[]patrs=s.split("-");

//parts[0]containsvalueofcol1,parts[1]containsvalueof

col2ofeachrow

Intentintent=newIntent(ListDataActivity.this,

EditSuppActivity.class);

//senddatatothenextactivity

intent.putExtra("col1Value",patrs[0]);

intent.putExtra("col2Value",patrs[1]);

intent.putExtra("col3Value",patrs[2]);

intent.putExtra("col4Value",patrs[3]);

startActivityForResult(intent,100);

finish();

}

});

Aprés avoir passer les données , on va les mettre dans des EditText inchangées ( setEnabled(false) ). EditTextinchangées (setEnabled(false)).

On crée 3 Boutons :mettre dans des EditText inchangées ( setEnabled(false) ). 1. Editer : pour activer la modification 2.

1. Editer : pour activer la modification

2. Enregistrer : pour sauvegarder les données (Mise à jour dans la base)

3. Supprimer :pour supprimer les données de la base.

4. Code xml du layout activity_edit_supp.xml:http://pastebin.com/RR17Uz5j

du layout activity_edit_supp.xml: http://pastebin.com/RR17Uz5j activity_edit_supp.xml Bon le démarche est le suivant :

activity_edit_supp.xml

Bon le démarche est le suivant :données de la base. 4. Code xml du layout activity_edit_supp.xml: http://pastebin.com/RR17Uz5j activity_edit_supp.xml

1.

Si l'utilisateur clique sur Supprimer(les autres sont desactivés) : les données seront supprimées de la base ==> AsyncTask

2. S'il clique sur Editer, les champs seront activées ainsi le bouton Enregistrer.

3. C'est un jeu de boutons

le bouton Enregistrer . 3. C'est un jeu de boutons A- SuppDataAsyncTask : AsyncTask pour supprimer

A- SuppDataAsyncTask : AsyncTask pour supprimer la ligne sélectionnée:

C'est presque comme les classes précédents !

C'est presque comme les classes précédents ! ==> On doit envoyer une valeur (paramètre : col1)

==> On doit envoyer une valeur (paramètre : col1) ==> HTTP POST Pour le retour de la requête:

: col1) ==> HTTP POST Pour le retour de la requête: ==> Un JSONObject avec un

==> Un JSONObject avec un entier et String

On déclare les variables globales nécessaires : urlSupp(url vers le fichiersuppression_bd.php ), success , message , progressDialog: suppression_bd.php), success , message , progressDialog:

supp.setOnClickListener(newOnClickListener(){

@Override

publicvoidonClick(Viewv){

//TODOAuto-generatedmethodstub

newSuppDataAsyncTask().execute();

});

}

& pour l'asyncTask :newSuppDataAsyncTask().execute(); }); } privateclassSuppDataAsyncTaskextends

privateclassSuppDataAsyncTaskextends AsyncTask<Void,Void,Void>{ @Override protectedvoidonPreExecute(){ Log.i("supp","onPreExecute"); super.onPreExecute(); progressDialog.show();

}

@Override

protectedVoiddoInBackground(Void

params){

Log.i("supp","startdoInBackground"); ServiceHandlersh=newServiceHandler();

List<NameValuePair>nameValuePair=newArrayList<NameValuePair>(1);

nameValuePair.add(new

BasicNameValuePair("col1",valCOL1.getText().toString()));

//Makingarequesttourlandgettingresponse

StringjsonStr=sh.makeServiceCall(urlSupp,

ServiceHandler.POST,nameValuePair);

Log.d("Response:",jsonStr);

if(jsonStr!=null){

try{

JSONObjectjsonObj=newJSONObject(jsonStr);

//returnvalueofsuccess

success=jsonObj.getInt("success");

message=jsonObj.getString("message");

Log.i("suucess",String.valueOf(success));

Log.i("message",message);

}catch(JSONExceptione){

e.printStackTrace();

}

}else{

Log.e("ServiceHandler","Couldn'tgetanydatafromtheurl");

}

Log.i("supp","enddoInBackground");

returnnull;

}

@Override

protectedvoidonPostExecute(Voidresult){

Log.i("supp","onPostExecute");

super.onPostExecute(result);

if(progressDialog.isShowing())

{

progressDialog.dismiss();

}

if(success==1)

{

Toast.makeText(getApplicationContext(),"Supprimé",

Toast.LENGTH_LONG).show();

}

else

{

Toast.makeText(getApplicationContext(),"Erreur",

Toast.LENGTH_LONG).show();

}

Intentintent=newIntent(EditSuppActivity.this,

ListDataActivity.class);

startActivityForResult(intent,100);

finish();

}

}

B- UpdateDataAsyncTask : AsyncTask pour mise à jour la ligne sélectionnée:

Cette asyncTask est similaire à l'asyncTask d'jout de données (mais dans la requéte en ajoute la valeur de col1)

données (mais dans la requéte en ajoute la valeur de col1) Je pense que vous avez

Je pense que vous avez appris le principe :D

Partie 3 : Conclusion:

Aprés ces deux parties , j'espére que vous avez appris une leçon trés important dans la vie professionelle (PFE , étude etc. ) .

Il y a beaucoup des notions , mais ce sont facile à comprendre , surtout que ce tutoriel est générale et

vous pouvez l'utiliser tel qu'il est en changants les champs (col1= id , col2= prix etc

Je vous souhaite bonne chance , et s'il y a une question , n'hesitez pas de nous contacter et poser vos questions soit par email (enisandroidclub@gmail.com) soit par un commentaire.

En fin je vous donne le lien de téléchargemnt du code source total: lien

)

soit par un commentaire . En fin je vous donne le lien de téléchargemnt du code
soit par un commentaire . En fin je vous donne le lien de téléchargemnt du code
soit par un commentaire . En fin je vous donne le lien de téléchargemnt du code