Contexte : Lister
le catalogue des formations de FormArmor, à partir du WebService
fournissant, au format JSON, la liste des formations.
Extrait du résultat généré par le webservice : :

1-
Créer le projet avec empty activity
2-
Au niveau du layout :
Dans mainActivity :
Effacer le TextView
"Hello World" de l'activité principale.
Convertir le
ConstraintLayout en CoordinatorLayout et le renommer
éventuellement..
Placer un ViewPager
dans ce CoordinatorLayout (le glisser dans la partie graphique et
non dans le "Component tree") et le renommer (exemple :
container).
Créer un
fragmentActivity :
New fragment
(fragment_blank)
Appeler la classe
GestionFragment et le layout fragment_main
Mettre les
différents composants dans le layoutFragment.xml pour obtenir ceci
:

|
<?xml
version="1.0"
encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GestionFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/txtLibelle"
android:layout_width="104dp"
android:layout_height="24dp"
android:text="Libellé
: "
tools:layout_editor_absoluteX="27dp"
tools:layout_editor_absoluteY="106dp"
/>
<EditText
android:id="@+id/edtLibelle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
tools:layout_editor_absoluteX="152dp"
tools:layout_editor_absoluteY="96dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/txtNiveau"
android:layout_width="104dp"
android:layout_height="31dp"
android:text="Niveau
: "
tools:layout_editor_absoluteX="27dp"
tools:layout_editor_absoluteY="173dp"
/>
<EditText
android:id="@+id/edtNiveau"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
tools:layout_editor_absoluteX="152dp"
tools:layout_editor_absoluteY="173dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/txtType"
android:layout_width="106dp"
android:layout_height="26dp"
android:text="Type
: "
tools:layout_editor_absoluteX="27dp"
tools:layout_editor_absoluteY="245dp"
/>
<EditText
android:id="@+id/edtType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
tools:layout_editor_absoluteX="152dp"
tools:layout_editor_absoluteY="245dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/txtDescription"
android:layout_width="8dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Description
: " />
<EditText
android:id="@+id/edtDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:inputType="textPersonName"
/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
|
Une classe gérant
ce fragment a été créée (GestionFragment.java). C'est cette
classe qui "implémentera" le layout correspondant :
fragment_main.xml.
Y supprimer les méthodes inutiles ici. C.A.D. conserver le constructeur, le newInstance, le onCreateView.
3
Travail préalable
3.1. Création de
la classe Formation
Pour faire le
mapping avec les données JSON il est utile de créer la classe
Formation :
package
metier;
public
class Formation
{
String libelle,
niveau,
type,
description;
int
coutRevient,
diplomante,
duree;
public
Formation()
{
}
public
Formation(String
libelle, String niveau, String type, String description,
int
coutRevient,
int
diplomante,
int
duree)
{
this.libelle
=
libelle;
this.niveau
=
niveau;
this.type
=
type;
this.description
=
description;
this.coutRevient
=
coutRevient;
this.diplomante
=
diplomante;
this.duree
=
duree;
}
public
String
getLibelle()
{
return
libelle;
}
public
void setLibelle(String
libelle)
{
this.libelle
=
libelle;
}
public
String
getNiveau()
{
return
niveau;
}
public
void setNiveau(String
niveau)
{
this.niveau
=
niveau;
}
public
String
getType()
{
return
type;
}
public
void setType(String
type)
{
this.type
=
type;
}
public
String
getDescription()
{
return
description;
}
public
void setDescription(String
description)
{
this.description
=
description;
}
public
int getCoutRevient()
{
return
coutRevient;
}
public
void setCoutRevient(int
coutRevient)
{
this.coutRevient
=
coutRevient;
}
public
int getDiplomante()
{
return
diplomante;
}
public
void setDiplomante(int
diplomante)
{
this.diplomante
=
diplomante;
}
public
int getDuree()
{
return
duree;
}
public
void setDuree(int
duree)
{
this.duree
=
duree;
}
}
3.2.
Modification du gradle et du manifest
Gradle :
apply
plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId
"com.example.philippe.android_listeformationavecswip"
minSdkVersion
15
targetSdkVersion
28
versionCode 1
versionName
"1.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
}
useLibrary 'org.apache.http.legacy'
buildTypes
{
release {
minifyEnabled false
proguardFiles
getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir:
'libs', include: ['*.jar'])
implementation
'com.android.support:appcompat-v7:28.0.0'
implementation
'com.android.support.constraint:constraint-layout:2.0.4'
implementation
'com.android.support:support-v4:28.0.0'
testImplementation
'junit:junit:4.12'
androidTestImplementation
'com.android.support.test:runner:1.0.2'
androidTestImplementation
'com.android.support.test.espresso:espresso-core:3.0.2'
}
Manifest :
<?xml
version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.philippe.android_listeformationavecswip">
<uses-permission
android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission
android:name="android.permission.INTERNET"
/>
<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"
/>
<category android:name="android.intent.category.LAUNCHER"
/>
</intent-filter>
</activity>
<uses-library android:name="org.apache.http.legacy"
android:required="false" />
</application>
</manifest>
4
Au niveau de la classe Java "MainActivity" :
Faire le lien entre le
ViewPager (de l'activité principale) et le fragmentActivity :
Nous allons utiliser un FragmentPageAdapter :
Dans
l'activité principale (MainActivity.java) créer une classe héritant
de FragmentPageAdapter et implémenter les méthodes
indispensables et générer le constructeur (sans le nombre de
pages).
Et ajouter :
// Méthode pour donner le nombre maximal de pages
int nbPages;
public void setCount(int nb)
{
nbPages = nb;
notifyDataSetChanged();
}
Déclarer en
variables globales :
private
SectionsPagerAdapter
mSectionsPagerAdapter;
private
ViewPager2
mViewPager;
SectionsPageAdapter
est le nom donné à cette classe interne.
Dans le onCreate
(après le setContentView(…)):
// Créer l'adaptateur
mSectionsPagerAdapter = new SectionsPagerAdapter(this);
// Récupérer le ViewPager2
mViewPager = (ViewPager2) findViewById(R.id.container);
// Paramétrer le ViewPager2 avec l'adaptateur.
mViewPager.setAdapter(mSectionsPagerAdapter);
Compléter cette classe java afin, entre
autres, de consommer le webservice (tâche asynchrone) fournissant
l'ensemble des formations. Voici le code complet :
package com.example.webserviceformationswip;
import android.os.AsyncTask;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import metier.Formation;
public class MainActivity extends AppCompatActivity
{
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager2 mViewPager;
private static ArrayList<Formation> lesFormations = new ArrayList<Formation>();
// L'ADRESSE IP SERA A REMPLACER PAR L'IP DU POSTE CONTENANT LE WEB SERVICE
String lien = "http://192.168.2.5:8080/WSFormationJSON/webresources/formationJSON";
URL urlCon;
HttpURLConnection urlConnection;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
// Version ViewPager
// mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Version ViewPager2
mSectionsPagerAdapter = new SectionsPagerAdapter(this);
// Paramétrage du ViewPager avec l'adaptateur
mViewPager = (ViewPager2) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
/* Lancement de la tache asynchrone (obligatoire car l'appel à un webservice est une "tache longue")*/
AccesWebServices accesWS = new AccesWebServices();
BufferedReader rd = null;
String retourWS = "";
try
{
//Récupération, conversion en String et exploitation de la valeur de retour
retourWS = accesWS.execute().get();
System.out.println("RETOUR:" + retourWS);
}
catch(Exception e)
{
System.out.println("ERREUR APPEL DU WEBSERVICE : " + e.getMessage());
}
/* PARSING DU TEXTE (retourWS) RETOURNE (format JSON) */
// Création du tableau JSON
JSONArray jTab = null;
try
{
jTab = new JSONArray(retourWS);
}
catch (Exception e)
{
System.out.println("ERREUR TABLEAU JSON : " + e.getMessage());
}
Formation uneFormation = null;
String libelle, niveau, type, description;
int coutRevient, diplomante, duree;
//MODIFICATIONS A FINIR
// Pour exploiter le tableau JSON
System.out.println("TAILLE TABLEAU:" + jTab.length());
for (int i=0; i < jTab.length(); i++)
{
try
{
libelle = jTab.getJSONObject(i).getString("libelle");
niveau = jTab.getJSONObject(i).getString("niveau");
type = jTab.getJSONObject(i).getString("type");
description = jTab.getJSONObject(i).getString("description");
coutRevient = jTab.getJSONObject(i).getInt("coutRevient");
diplomante = jTab.getJSONObject(i).getInt("diplomante");
duree = jTab.getJSONObject(i).getInt("duree");
uneFormation = new Formation(libelle, niveau, type, description, coutRevient, diplomante, duree);
}
catch (JSONException jse)
{
System.out.println("ERREUR OBJET JSON : " + jse.getMessage());
}
lesFormations.add(uneFormation);
}
/* FIN DU PARSING */
// On va compter le nombre d'écrans sachant que nombre de formations = nombre d'écrans.
int max = lesFormations.size();
mSectionsPagerAdapter.setCount(max);
}
//private class SectionsPagerAdapter extends FragmentPagerAdapter
private class SectionsPagerAdapter extends FragmentStateAdapter
{
int nbPages;
/*
public SectionsPagerAdapter(FragmentManager fm)
{
super(fm);
}
*/
// Version ViewPager2
public SectionsPagerAdapter(FragmentActivity fm)
{
super(fm);
}
/*
// Version ViewPager
@Override
public Fragment getItem(int position)
{
// getItem est appelé pour instancier le fragment pour la page donnée.
// Retourne un GestionFragment (défini comme une classe interne plus bas).
return GestionFragment.newInstance(position + 1);
}
*/
// Version ViewPager2
@Override
public Fragment createFragment(int position)
{
// createFragment est appelé pour instancier le fragment pour la page donnée.
// Retourne un GestionFragment (défini comme une classe interne plus bas).
return GestionFragment.newInstance(position + 1);
}
/*
// Version ViewPager
@Override
public int getCount()
{
// Show nbPages total pages.
return nbPages;
}
*/
// Version ViewPager2
@Override
public int getItemCount()
{
// Show nbPages total pages.
return nbPages;
}
public void setCount(int nb)
{
nbPages = nb;
notifyDataSetChanged();
}
}
//private class AccesWebServices extends AsyncTask<Void, Void, HttpResponse>
private class AccesWebServices extends AsyncTask<Void, Void, String>
{
@Override
protected void onPreExecute()
{
super.onPreExecute();
Toast.makeText(getApplicationContext(), "Début du traitement asynchrone", Toast.LENGTH_LONG).show();
}
@Override
//protected HttpResponse doInBackground(Void... params)
protected String doInBackground(Void... params)
{
try
{
int statusCode = 0;
String ret;
urlCon = new URL(lien);
urlConnection = (HttpURLConnection) urlCon.openConnection();
statusCode = urlConnection.getResponseCode();
System.out.println("STATUS : " + statusCode);
if (statusCode == 200)
{
InputStream in = urlConnection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(in));
ret = rd.readLine();
System.out.println("PASSAGE1 : " + ret);
}
else
{
ret = null;
}
return ret;
}
catch (Exception ex)
{
System.out.println("ERREUR ASYNCTASK : " + ex.getMessage());
return null;
}
}
/*
@Override
protected void onPostExecute(OutputStreamWriter resultat)
{
Toast.makeText(getApplicationContext(), "Fin du traitement asynchrone", Toast.LENGTH_LONG).show();
}
*/
}
}
Ne pas tenir
compte de l'erreur et
Compléter la
classe GestionFragment.java (voir ci-dessous) pour
renseigner le layout fragment_main.java.
Pour éviter de refaire 2 fois la tache
asynchrone, on va mettre la classe GestionFragment en classe
interne de fragment_main.java. Il suffit de cliquer sur
GestionFragment.java, dans la partie gauche, et de glisser sur
fragment_main.java. Puis cliquer sur Refactor.
Code complet final de MainActivity.java
:
package com.example.webserviceformationswip;
import android.os.AsyncTask;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import metier.Formation;
public class MainActivity extends AppCompatActivity
{
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager2 mViewPager;
private static ArrayList<Formation> lesFormations = new ArrayList<Formation>();
// L'ADRESSE IP SERA A REMPLACER PAR L'IP DU POSTE CONTENANT LE WEB SERVICE
String lien = "http://192.168.2.5:8080/WSFormationJSON/webresources/formationJSON";
URL urlCon;
HttpURLConnection urlConnection;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
// Version ViewPager
// mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Version ViewPager2
mSectionsPagerAdapter = new SectionsPagerAdapter(this);
// Paramétrage du ViewPager avec l'adaptateur
mViewPager = (ViewPager2) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
/* Lancement de la tache asynchrone (obligatoire car l'appel à un webservice est une "tache longue")*/
AccesWebServices accesWS = new AccesWebServices();
BufferedReader rd = null;
String retourWS = "";
try
{
//Récupération, conversion en String et exploitation de la valeur de retour
retourWS = accesWS.execute().get();
System.out.println("RETOUR:" + retourWS);
}
catch(Exception e)
{
System.out.println("ERREUR APPEL DU WEBSERVICE : " + e.getMessage());
}
/* PARSING DU TEXTE (retourWS) RETOURNE (format JSON) */
// Création du tableau JSON
JSONArray jTab = null;
try
{
jTab = new JSONArray(retourWS);
}
catch (Exception e)
{
System.out.println("ERREUR TABLEAU JSON : " + e.getMessage());
}
Formation uneFormation = null;
String libelle, niveau, type, description;
int coutRevient, diplomante, duree;
//MODIFICATIONS A FINIR
// Pour exploiter le tableau JSON
System.out.println("TAILLE TABLEAU:" + jTab.length());
for (int i=0; i < jTab.length(); i++)
{
try
{
libelle = jTab.getJSONObject(i).getString("libelle");
niveau = jTab.getJSONObject(i).getString("niveau");
type = jTab.getJSONObject(i).getString("type");
description = jTab.getJSONObject(i).getString("description");
coutRevient = jTab.getJSONObject(i).getInt("coutRevient");
diplomante = jTab.getJSONObject(i).getInt("diplomante");
duree = jTab.getJSONObject(i).getInt("duree");
uneFormation = new Formation(libelle, niveau, type, description, coutRevient, diplomante, duree);
}
catch (JSONException jse)
{
System.out.println("ERREUR OBJET JSON : " + jse.getMessage());
}
lesFormations.add(uneFormation);
}
/* FIN DU PARSING */
// On va compter le nombre d'écrans sachant que nombre de formations = nombre d'écrans.
int max = lesFormations.size();
mSectionsPagerAdapter.setCount(max);
}
//private class SectionsPagerAdapter extends FragmentPagerAdapter
private class SectionsPagerAdapter extends FragmentStateAdapter
{
int nbPages;
/*
public SectionsPagerAdapter(FragmentManager fm)
{
super(fm);
}
*/
// Version ViewPager2
public SectionsPagerAdapter(FragmentActivity fm)
{
super(fm);
}
/*
// Version ViewPager
@Override
public Fragment getItem(int position)
{
// getItem est appelé pour instancier le fragment pour la page donnée.
// Retourne un GestionFragment (défini comme une classe interne plus bas).
return GestionFragment.newInstance(position + 1);
}
*/
// Version ViewPager2
@Override
public Fragment createFragment(int position)
{
// createFragment est appelé pour instancier le fragment pour la page donnée.
// Retourne un GestionFragment (défini comme une classe interne plus bas).
return GestionFragment.newInstance(position + 1);
}
/*
// Version ViewPager
@Override
public int getCount()
{
// Show nbPages total pages.
return nbPages;
}
*/
// Version ViewPager2
@Override
public int getItemCount()
{
// Show nbPages total pages.
return nbPages;
}
public void setCount(int nb)
{
nbPages = nb;
notifyDataSetChanged();
}
}
//private class AccesWebServices extends AsyncTask<Void, Void, HttpResponse>
private class AccesWebServices extends AsyncTask<Void, Void, String>
{
@Override
protected void onPreExecute()
{
super.onPreExecute();
Toast.makeText(getApplicationContext(), "Début du traitement asynchrone", Toast.LENGTH_LONG).show();
}
@Override
//protected HttpResponse doInBackground(Void... params)
protected String doInBackground(Void... params)
{
try
{
int statusCode = 0;
String ret;
urlCon = new URL(lien);
urlConnection = (HttpURLConnection) urlCon.openConnection();
statusCode = urlConnection.getResponseCode();
System.out.println("STATUS : " + statusCode);
if (statusCode == 200)
{
InputStream in = urlConnection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(in));
ret = rd.readLine();
System.out.println("PASSAGE1 : " + ret);
}
else
{
ret = null;
}
return ret;
}
catch (Exception ex)
{
System.out.println("ERREUR ASYNCTASK : " + ex.getMessage());
return null;
}
}
/*
@Override
protected void onPostExecute(OutputStreamWriter resultat)
{
Toast.makeText(getApplicationContext(), "Fin du traitement asynchrone", Toast.LENGTH_LONG).show();
}
*/
}
public static class GestionFragment extends Fragment
{
EditText edtLibelle, edtNiveau, edtType, edtDescription;
int positionCourante=0;
public GestionFragment()
{
}
/**
* Retourne une instance de ce fragment pour le n° de section demandé
*/
public static GestionFragment newInstance(int sectionNumber)
{
GestionFragment fragment = new GestionFragment();
Bundle args = new Bundle();
args.putInt("num_etape", sectionNumber);
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// Pour faire le lien entre le layout fragment_main et le conteneur de l'activity_main
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Récupération du numéro d'écran
int numEcran = getArguments().getInt("num_etape");
System.out.println("NUM ECRAN:" + numEcran);
positionCourante = numEcran - 1; // -1 car la position courante commence à 0 et pas numEcran
initialisations(rootView);
affichage();
return rootView;
}
public void initialisations(View rootView)
{
edtLibelle = rootView.findViewById(R.id.edtLibelle);
edtNiveau = rootView.findViewById(R.id.edtNiveau);
edtType = rootView.findViewById(R.id.edtType);
edtDescription = rootView.findViewById(R.id.edtDescription);
}
public void affichage()
{
Formation uneFormation = lesFormations.get(positionCourante);
edtLibelle.setText(uneFormation.getLibelle());
edtNiveau.setText(uneFormation.getNiveau());
edtType.setText(uneFormation.getType());
edtDescription.setText(uneFormation.getDescription());
}
}
}
Vous pouvez tester après avoir lancé le
WebService concerné.