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 : :

                image

 

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 :

image

<?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é.