Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Android Part5-2

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 44

Android - Semaine 4

Adaptateur personnalisé

Adaptateur personnalisé

29 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Classe Adapter personnalisée

Parce que ArrayAdapter n’affiche qu’un seul texte, nous allons


définir notre propre adaptateur : ProductAdapter.
Il faut le faire hériter de ArrayAdapter<Product> pour ne pas
tout reprogrammer :
public class ProductAdapter extends ArrayAdapter<Product> {

public ProductAdapter(Context ctx, List<Product>


products) {
super(ctx, 0, products);
}

Source biblio : http://www.bignerdranch.com/blog/


customizing-android-listview-rows-subclassing

30 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Classe Adapter perso, suite

Sa principale méthode est getView qui crée les vues pour le


ListView. Elle retourne une disposition, p. ex. un
RelativeLayout contenant des TextView et ImageView.

public
View getView(int position, View recup, ViewGroup parent);

position est le numéro, dans le ListView, de l’item à afficher.


recup est une ancienne vue devenue invisible dans le ListView.
Voir transpa suivant.
NB: ce paramètre s’appelle convertView dans les docs.
parent : le ListView auquel sera rattaché cette vue.

31 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Réutilisation d’une vue

À l’écran, un ListView ne peut afficher que quelques éléments en


même temps. On peut faire défiler vers le haut ou vers le bas pour
voir les autres.
Au lieu d’instancier autant de layouts que d’éléments dans la liste,
un ListView réutilise ceux qui deviennent invisibles à cause du
défilement. Par exemple, quand on défile vers le haut pour voir plus
bas, les éléments du haut disparaissent de la vue ; on peut donc les
réutiliser en changeant leur contenu et en les affichant en dessous.
C’est comme un escalator : les marches qui arrivent à un bout
reviennent au début.
Le paramètre appelé recup (ou convertView) est une telle vue
réutilisée. S’il est null, on doit créer la vue pour cet item, sinon on
doit reprendre recup et changer son contenu.
32 / 72 Pierre Nerzic
Recyclage des vues

Une vue qui devient invisible d’un côté, à cause du scrolling, est
renvoyée de l’autre côté, comme sur un tapis roulant, en modifiant
seulement son contenu :

Il suffit de remplacer « Prod1» par « Prod7» et de mettre la vue


en bas.
Android - Semaine 4
Adaptateur personnalisé

Méthode getView personnalisée

Voici donc la surcharge de cette méthode :


@Override
public
View getView(int position, View recup, ViewGroup parent)
{
// créer ou réutiliser un ProductView
ProductView itemView = (ProductView) recup;
if (itemView == null)
itemView = ProductView.create(parent); // <==(!!)

// afficher les valeurs


itemView.setItem(super.getItem(position));
return itemView;
}

33 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Méthode ProductView.create
Cette méthode crée une instance de ProductView qui est un
groupe de vues pour afficher un item des données.
La méthode ProductAdapter.getView crée des
ProductView à la demande du ListView,
Un ProductView est une sorte deRelativeLayout
contenant des TextView etImageView

Cette disposition est définie par un fichier layout XML


res/layout/item.xml.
Dans le ListView, on va avoir plusieurs instances de ProductView,
chacune pour un élément de la liste.
34 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Layout d’item res/layout/item.xml


C’est subtil : on va remplacer la racine du layout des items, un
RelativeLayout par une classe personnalisée :
<?xml version="1.0" encoding="utf-8"?>
<ma.ceft.products.ProductView
xmlns:android="..."
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

Et cette classe ProductView hérite de RelativeLayout :


package .......
public class ProductView extends RelativeLayout
{
...

35 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Classe personnalisée dans les ressources

Android permet d’utiliser les classes de notre application à l’intérieur


d’un layout. Il suffit de les préfixer par le package.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="..."
android:layout_width="match_parent"
android:layout_height="wrap_content">
<...........................MaVuePerso
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

La classe MaVuePerso doit hériter de View et implémenter


certaines méthodes.

36 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Classe ProductView pour afficher les items

Cette classe a pour but de gérer les vues dans lesquelles il y a les
informations des produits : Intitulé, prix, description.
On la met à la place de la balise RelativeLayout :
<?xml version="1.0" encoding="utf-8"?>
<ma.ceft.products.ProductView ...>
<ImageView android:id="@+id/item_image" .../>
<TextView android:id="@+id/item_libelle" .../>
<TextView android:id="@+id/item_prix" .../>
</ma.ceft.products.ProductView>

Les propriétés de placement restent les mêmes. ProductView est


seulement une sous-classe de RelativeLayout avec quelques
variables d’instance et méthodes de plus.

37 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Définition de la classe ProductView

Le constructeur de ProductView est nécessaire, mais quasi-vide :


public class ProductView extends RelativeLayout
{
public ProductView(Context context, ...) {
super(context, attrs);
}

Tout se passe dans la méthode de classe ProductView.create


appelée par l’adaptateur. Rappel de la page 33 :
// créer ou réutiliser un ProductView
ProductView itemView = (ProductView) recup;
if (itemView == null) itemView = ProductView.create(parent);
...

Cette méthode create génère les vues du layout item.xml.


38 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Créer des vues à partir d’un layout XML

La génération de vues pour afficher les items repose sur un


mécanisme appelé LayoutInflater qui fabrique des vues Android
(objets Java) à partir d’un layout XML :

LayoutInflater li = LayoutInflater.from(context);
View itemView = li.inflate(R.layout.item, parent);

On lui fournit l’identifiant du layout, p. ex. celui des items. Elle crée
les vues spécifiées dans res/layout/item.xml.
context est l’activité qui affiche toutes ces vues,
parent est la vue qui doit contenir ces vues, null si aucune.

39 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Méthode ProductView.create

La méthode de classe ProductView.create expanse le layout des


items à l’aide d’un LayoutInflater :
public static ProductView create(ViewGroup parent)
{
LayoutInflater li =
LayoutInflater.from(parent.getContext());
ProductView itemView = (ProductView)
li.inflate(R.layout.item, parent, false);
itemView.findViews();
return itemView;
}

static signifie qu’on appelle cette méthode sur la classe elle-même


et non pas sur une instance. C’est une méthode de classe.
40 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Méthode findViews

Cette méthode a pour but de récupérer les objets Java


correspondant aux TextView et ImageView de l’item. Ils sont
placés dans des variables de la classe :

// vues du layout item_product.xml


private TextView tvLibelle;
private TextView tvPrix;
private ImageView ivImage;

private void findViews()


{
tvLibelle = findViewById(R.id.item_libelle);
tvPrix = findViewById(R.id.item_prix);
ivImage = findViewById(R.id.item_image);
}

41 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Pour finir, la méthode ProductView.setItem

Son rôle est d’afficher les informations d’un produit dans


lesTextView et ImageView de l’item.

public void setItem(final Product product)


{
tvLibelle.setText(product.getLibelle());
tvPrix.setText(product.getPrix()+" Dhs");
ivImage.setImageResource(product.getIdImage());
}

Elle utilise les getters de la classe Product : getLibelle. . .

42 / 72 Pierre Nerzic
Android - Semaine 4
Adaptateur personnalisé

Récapitulatif

Voici la séquence qui amène à l’affichage d’un item dans la liste :


1. Le ListView appelle la méthode getView(position, ...)
de l’adaptateur, position est le no de l’élément concerné,
2. L’adaptateur appelle éventuellement ProductView.create
: a.ProductView.create fait instancier item.xml = une
sous-classe de RelativeLayout appelée ProductView.
b. Cela crée les vues libelle, prix et image pour lesquelles
ProductView.findViews récupère les objets Java.
3. L’adaptateur appelle la méthode setItem du ProductView
avec les données à afficher.
a. ProductView.setItem appelle setText des vues pour
afficher les valeurs.

43 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Actions utilisateur sur la liste

45 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Modification des données

Les modifications sur les données doivent se faire par les méthodes
add, insert, remove et clear de l’adaptateur. Voir la doc.
Si ce n’est pas possible, par exemple parce qu’on a changé d’activité
et modifié les données sans adaptateur, alors au retour, par exemple
dans onActivityResult, il faut prévenir l’adaptateur par la
méthode suivante :
adapter.notifyDataSetChanged();

Si on néglige de le faire, alors la liste affichée à l’écran ne changera


pas.

46 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Clic sur un élément

Voyons le traitement des sélections utilisateur sur une liste. La


classe ListActivity définit déjà un écouteur pour les clics. Il
suffit de le surcharger :

@Override
public void onListItemClick (
ListView l, View v, int position, long idvue)
{
// gérer un clic sur l'item identifié par position
...
}

Par exemple, créer un Intent pour afficher ou éditer l’item. Ne pas


oublier d’appeler adapter.notifyDataSetChanged() au retour.

47 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Clic sur un élément, suite

Si votre activité est une simple Activity (parce qu’il y a autre


chose qu’une liste, ou plusieurs listes), alors c’est plus complexe :
Votre activité doit implémenter l’interface
AdapterView.OnItemClickListener,
Vous devez définir this en tant qu’écouteur du ListView,
Votre activité doit surcharger la méthode onItemClick.

48 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Clic sur un élément, suite

public class MainActivity extends Activity


implements OnItemClickListener
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
// appeler la méthode surchargée dans la superclasse
super.onCreate(savedInstanceState);

// mettre en place le layout contenant le ListView


setContentView(R.layout.main);
ListView lv = findViewById(android.R.id.list);
lv.setOnItemClickListener(this);
}

49 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Clic sur un élément, fin

Et voici sa méthode onItemClick :


@Override
public void onItemClick(
AdapterView<?> parent, View v, int position, long idvue)
{
// gérer un clic sur l'item identifié par position
...
}

Il existe aussi la méthode boolean onItemLongClick ayant les


mêmes paramètres, installée par setOnItemLongClickListener.

50 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Liste d’éléments cochables

Android offre des listes cochables comme celles-ci :

Le style de la case à cocher dépend du choix unique ou multiple.

51 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Liste cochable simple

Android propose un layout prédéfini pour items cochables :

@Override
protected void onCreate(Bundle savedInstanceState)
{
...
setListAdapter(
new ArrayAdapter<>(this
android.R.layout.simple_list_item_checked,
android.R.id.text1, mListe));

ListView lv = findViewById(android.R.id.list);
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}

52 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Liste à choix multiples

Toujours avec des listes prédéfinies, c’est une simple variante :


mettre simple_list_item_multiple_choice à la place de
simple_list_item_checked,
mettre ListView.CHOICE_MODE_MULTIPLE au lieu de
ListView.CHOICE_MODE_SINGLE.
La méthode onListItemClick est appelée sur chaque élément
cliqué.

53 / 72 Pierre Nerzic
Android - Semaine 4
Actions utilisateur sur la liste

Liste cochable personnalisée

Si on veut un layout personnalisé comme ProductView, il faut


quesa classe implémente l’interface Checkable càd 3 méthodes :
public boolean isChecked() indique si l’item est coché
public void setChecked(boolean etat) doit changer
l’état interne de l’item
public void toggle() doit inverser l’état interne de l’item
Il faut rajouter un booléen dans chaque item, celui que j’ai appelé
état interne.
D’autre part, dans le layout d’item, il faut employer un
CheckedTextView même vide, plutôt qu’un CheckBox qui ne
réagit pas aux clics (bug Android).

54 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

RecyclerView

55 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Présentation

Ce type de vue affiche également une liste, comme un ListView. Il


s’appuie également sur un adaptateur, et repose sur un mécanisme
très similaire à la classe ProductView qu’on appelle view holder.
Les concepts du RecyclerView sont repris dans de nombreux
autres contrôles Android : les boîtes à onglets, etc.
NB: cette vue appartient à la bibliothèque support, voir au prochain
cours. Il faut rajouter ceci dans le build.gradle du projet :
implementation 'com.android.support:recyclerview-v7:27.1.1'

NB: le numéro de version correspond au SDK installé.

56 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Principes

Comme pour un ListView, vous devez définir deux layouts :


main.xml pour l’activité. Mettez RecyclerView au lieu de
ListView
item.xml pour les items de la liste.
Comme précédemment, la méthode onCreate de l’activité crée un
adaptateur pour les données et l’associe au RecyclerView. Ce qui
change, c’est l’adaptateur ProductAdapter et la classe liée aux
items ProductView.

57 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Méthode onCreate de l’activité

public class MainActivity extends AppCompatActivity {


@Override protected void onCreate(Bundle sIS) {
// mettre en place l'interface
super.onCreate(sIS);
setContentView(R.layout.main);
// données
List<Product> liste = new ArrayList<>();
...
// mettre en place la liste
RecyclerView recyclerView = findViewById(R.id.list);
recyclerView.setAdapter(new ProductAdapter(liste));
recyclerView.setHasFixedSize(true);
}

58 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Disposition des éléments

Pour un affichage correct des éléments, il manque quelques


instructions. Il faut spécifier un gestionnaire de mise en page :
import android.support.v7.widget.LinearLayoutManager;

...
LinearLayoutManager llm = new LinearLayoutManager(this);
recyclerView.setLayoutManager(llm);

On peut indiquer l’axe de défilement de la liste :


LinearLayoutManager llm =
new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL);

59 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Mise en grille

Au lieu d’un LinearLayoutManager, on peut créer un


GridLayoutManager qui fait une grille d’un certain nombre de
colonnes indiqué en paramètre :
import android.support.v7.widget.GridLayoutManager;

...
GridLayoutManager glm = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(glm);
recyclerView.setHasFixedSize(true);

On peut aussi indiquer l’axe de défilement de la liste :


GridLayoutManager glm =
new GridLayoutManager(this,
2,
LinearLayoutManager.HORIZONTAL);
60 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Séparateur entre items


Par défaut, un RecyclerView n’affiche pas de ligne de séparation
entre les élements. Pour en avoir :
import android.support.v7.widget.DividerItemDecoration;

...
DividerItemDecoration dividerItemDecoration =
new DividerItemDecoration(recyclerView.getContext(),
llm.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);

Voir ce lien sur stackoverflow pour un séparateur dans une grille.

61 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Adaptateur de RecyclerView

Voici la nouvelle définition pour un RecyclerView :

public class ProductAdapter


extends RecyclerView.Adapter<ProductView>
{
private final List<Product> mListe;

ProductAdapter(List<Product> liste)
{
mListe = liste;
}

C’est une classe qui hérite de RecyclerView.Adapter. Son


constructeur mémorise la liste des données dans une variable privée.

62 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Adaptateur de RecyclerView, suite

Parmi les méthodes à définir, il faut surcharger la méthode


getItemCount ; elle retourne le nombre d’éléments de la
collection :

@Override
public int getItemCount()
{
return mListe.size();
}

Dans le cas plus général où les données ne sont pas stockées dans
une liste, cela peut entraîner un calcul plus complexe.

63 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Adaptateur de RecyclerView, suite

Deux autres méthodes doivent être surchargées, voici la première :

@Override
public void onBindViewHolder(ProductView itemView, int pos)
{
itemView.setItem(mListe.get(pos));
}

Son rôle est de remplir un ProductView avec la donnée spécifiéepar


la position. La classe ProductView est légèrement différente dela
version pour ListView, voir plus loin.

64 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Adaptateur de RecyclerView, suite

La dernière méthode à surcharger est très simple :


@Override
publicProductView onCreateViewHolder(
ViewGroup parent, int viewType)
{
LayoutInflater li =
LayoutInflater.from(parent.getContext());
View itemView =
li.inflate(R.layout.item, parent, false);
return new ProductView(itemView); }

Le rôle de cette méthode est le même que create d’un


ProductView page 40. Elle expanse les vues pour afficher un item.
La réutilisation d’un item est gérée automatiquement par Android.
65 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Affichage d’un item ProductView


C’est une variante de celle du ListView, les explis sont après :
class ProductView extends RecyclerView.ViewHolder {
private TextView tvLibelle;
...
ProductView(View view) {
super(view);
findViews(view);
}
private void findViews(View view) {
tvLibelle = view.findViewById(R.id.item_libelle);
...
}
void setItem(final Product product) {
...
}
}
66 / 72 Pierre Nerzic
Android - Semaine 4
RecyclerView

Affichage d’un item ProductView, suite

Le constructeur est appelé par onCreateViewHolder de


l’adaptateur. Il reçoit une View en paramètre, c’est la racine
du layout d’item, donc un RelativeLayout ici.
La méthode findViews permet d’associer des objets Java aux
vues du layout d’item.
Il devient donc inutile de mentionner une classe perso dans le
layout d’item. Ce layout redevient « normal » :

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout ...>
<ImageView android:id="@+id/item_image" .../>
<TextView android:id="@+id/item_libelle" .../>
<TextView android:id="@+id/item_prix" .../>
</RelativeLayout>

67 / 72 Pierre Nerzic
Android - Semaine 4
Réponses aux sélections d’items

Réponses aux sélections d’items

68 / 72 Pierre Nerzic
Android - Semaine 4
Réponses aux sélections d’items

Clics sur un RecyclerView


Petit problème : il n’y a pas d’écouteur pour les clics sur les
éléments, comme avec un ListView. Une solution simple passe par
la méthode onBindViewHolder de l’adaptateur :
@Override
public void onBindViewHolder(ProductView holder, final int pos)
{
final Product product = mProducts.get(pos);
holder.setProducts(product);
holder.itemView.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO l'action à faire sur l'item n° pos
}
});
}
69 / 72 Pierre Nerzic
Android - Semaine 4
Réponses aux sélections d’items

Afficher la sélection

Pour mettre un item en évidence dans un RecyclerView, il faut


gérer la position désignée par un clic :
public class ProductAdapter extends RecyclerView.Adapter<ProductVie

...
int clickedPosition = RecyclerView.NO_POSITION;

public int getClickedPosition() {


return clickedPosition;
}
private void setClickedPosition(int pos) {
notifyItemChanged(clickedPosition);
clickedPosition = pos;
notifyItemChanged(clickedPosition);
}

70 / 72 Pierre Nerzic
Android - Semaine 4
Réponses aux sélections d’items

Afficher la sélection, suite

Ensuite, on modifie onBindViewHolder :


public void onBindViewHolder(ProductView holder, final int pos)
{
...
holder.itemView.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
setClickedPosition(pos);
...
}
});
// accentuer la sélection
holder.itemView.setBackgroundColor(
getClickedPosition() == pos ?
Color.LTGRAY : Color.TRANSPARENT);
}
71 / 72 Pierre Nerzic
Android - Semaine 4
Réponses aux sélections d’items

Ouf, c’est fini

C’est tout pour cette semaine. La semaine prochaine nous parlerons


des menus, dialogues et fragments.

72 / 72 Pierre Nerzic

Vous aimerez peut-être aussi