• Aucun résultat trouvé

La liste d’évaluation de la section précédente fonctionne, mais son implémentation est très lourde. Pire, l’essentiel de ce code ennuyeux ne sera pas réutilisable, sauf dans des circonstances très limitées.

Nous pouvons mieux faire.

En fait, nous voudrions pouvoir créer un layout comme celui-ci :

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

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent" >

<TextView

android:id="@+id/selection"

android:layout_width="fill_parent"

android:layout_height="wrap_content"/>

<com.commonsware.android.fancylists.seven.RateListView android:id="@android:id/list"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:drawSelectorOnTop="false"

/>

</LinearLayout>

Figure 9.4

La même application, montrant un mot avec la note maximale.

où toute la logique du code qui utilisait une ListView auparavant "fonctionnerait" avec la RateListView du layout :

public class RateListViewDemo extends ListActivity { TextView selection;

String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"};

@Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.main);

setListAdapter(new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1, items));

selection=(TextView)findViewById(R.id.selection);

}

public void onListItemClick(ListView parent, View v, int position, long id) { selection.setText(items[position]);

} }

Les choses se compliquent un tout petit peu lorsqu’on réalise que, jusqu’à maintenant, les codes de ce chapitre n’ont jamais réellement modifié la ListView elle-même. Nous n’avons fait que travailler sur les adaptateurs, en redéfinissant getView(), en créant nos propres lignes par inflation, etc.

Si l’on souhaite que RateListView prenne n’importe quel ListAdapter et fonctionne

"comme il faut", en plaçant les barres d’évaluation sur les lignes comme il se doit, nous devons faire un peu de gymnastique. Plus précisément, nous devons envelopper le List-Adapter "brut" dans un autre, qui sait comment placer les barres dans les lignes et mémo-riser l’état de ces barres.

Nous devons d’abord établir le motif de conception dans lequel un ListAdapter en augmente un autre. Voici le code d’AdapterWrapper, qui prend en charge un ListAdapter et délègue toutes les méthodes de l’interface à delegate. Vous retrouverez ce code dans le projet FancyLists/RateListView :

public class AdapterWrapper implements ListAdapter { ListAdapter delegate=null;

public AdapterWrapper(ListAdapter delegate) { this.delegate=delegate;

}

Chapitre 9 S’amuser avec les listes 101

public int getCount() { return(delegate.getCount());

}

public Object getItem(int position) { return(delegate.getItem(position));

}

public long getItemId(int position) { return(delegate.getItemId(position));

}

public View getView(int position, View convertView, ViewGroup parent) {

return(delegate.getView(position, convertView, parent));

}

public void registerDataSetObserver(DataSetObserver observer) { delegate.registerDataSetObserver(observer);

}

public boolean hasStableIds() { return(delegate.hasStableIds());

}

public boolean isEmpty() { return(delegate.isEmpty());

}

public int getViewTypeCount() { return(delegate.getViewTypeCount());

}

public int getItemViewType(int position) { return(delegate.getItemViewType(position));

}

public void unregisterDataSetObserver(DataSetObserver observer) { delegate.unregisterDataSetObserver(observer);

}

public boolean areAllItemsEnabled() { return(delegate.areAllItemsEnabled());

}

public boolean isEnabled(int position) { return(delegate.isEnabled(position));

} }

Nous pouvons alors hériter d’AdapterWrapper pour créer RateableWrapper, qui redéfinit la méthode getView() tout en laissant le ListAdapter délégué faire "le vrai travail" :

public class RateableWrapper extends AdapterWrapper { Context ctxt=null;

float[] rates=null;

public RateableWrapper(Context ctxt, ListAdapter delegate) { super(delegate);

this.ctxt=ctxt;

this.rates=new float[delegate.getCount()];

for (int i=0;i<delegate.getCount();i++) { this.rates[i]=2.0f;

} }

public View getView(int position, View convertView, ViewGroup parent) {

ViewWrapper wrap=null;

View row=convertView;

if (convertView==null) {

LinearLayout layout=new LinearLayout(ctxt);

RatingBar rate=new RatingBar(ctxt);

rate.setNumStars(3);

rate.setStepSize(1.0f);

View guts=delegate.getView(position, null, parent);

layout.setOrientation(LinearLayout.HORIZONTAL);

rate.setLayoutParams(new LinearLayout.LayoutParams(

LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT));

guts.setLayoutParams(new LinearLayout. LayoutParams(

LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));

RatingBar.OnRatingBarChangeListener l=

new RatingBar.OnRatingBarChangeListener() { public void onRatingChanged(RatingBar ratingBar, float rating,

Chapitre 9 S’amuser avec les listes 103

rate.setOnRatingBarChangeListener(l);

rate.setTag(new Integer(position));

rate.setRating(rates[position]);

row=layout;

} else {

wrap=(ViewWrapper)convertView. getTag();

wrap.setGuts(delegate.getView(position, wrap.getGuts(), parent));

wrap.getRatingBar().setTag(new Integer(position));

wrap.getRatingBar().setRating(rates[position]);

}

return(row);

} }

L’essentiel du traitement de notre liste d’évaluation réside dans RateableWrapper. Cette classe place les barres d’évaluation sur les lignes et mémorise leurs états lorsqu’elles sont modifiées par l’utilisateur. Pour stocker ces états, on utilise un tableau de float dont la taille correspond au nombre de lignes que delegate rapporte pour cette liste.

L’implémentation de la méthode getView() de RateableWrapper rappelle celle de Rate-ListDemo, sauf qu’au lieu d’utiliser LayoutInflater on doit construire manuellement un conteneur LinearLayout pour y placer notre RatingBar et les "tripes" (c’est-à-dire toutes les vues créées par delegate et que l’on décore avec une barre d’évaluation). LayoutIn-flater est conçue pour construire une View à partir de widgets bruts or, ici, nous ne savons pas par avance à quoi ressembleront les lignes, hormis le fait qu’il faudra leur ajouter une barre d’évaluation. Le reste du code est semblable à celui de RateListDemo :

class ViewWrapper { ViewGroup base;

View guts=null;

RatingBar rate=null;

ViewWrapper(ViewGroup base) { this.base=base;

}

RatingBar getRatingBar() { if (rate==null) {

rate=(RatingBar)base.getChildAt(0);

}

return(rate);

}

void setRatingBar(RatingBar rate) { this.rate=rate;

Lorsque tout ceci est en place, le code de RateListView devient assez simple :

public class RateListView extends ListView { public RateListView(Context context) { super(context);

}

public RateListView(Context context, AttributeSet attrs) { super(context, attrs);

}

public RateListView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

}

public void setAdapter(ListAdapter adapter) {

super.setAdapter(new RateableWrapper(getContext(), adapter));

} }

On hérite simplement de ListView et on redéfinit setAdapter() pour pouvoir envelopper dans notre propre RateableWrapper le ListAdapter fourni en paramètre.

Chapitre 9 S’amuser avec les listes 105

Comme le montre la Figure 9.5, les résultats sont identiques à ceux de RateListDemo, sauf que les mots ayant la note maximale n’apparaissent plus en majuscules.

La différence est la réutilisabilité. Nous pourrions créer un paquetage JAR de RateListView et l’insérer dans n’importe quel projet Android qui en a besoin. Par conséquent, bien que RateListView soit un peu plus compliquée à écrire, il ne faut plus le faire qu’une seule fois et le reste du code de l’application est merveilleusement simple.

Cette classe RateListView pourrait, bien sûr, proposer des fonctionnalités supplémen-taires, comme la possibilité de modifier par programmation l’état des barres (en mettant à jour le tableau de float et la RatingBar elle-même), autoriser l’appel d’un autre code lorsque l’état d’une barre est modifié (via une fonction de rappel), etc. Nous les laissons en exercice au lecteur.

Documents relatifs