Principe
- Classes dérivées de AdapterView
-
Adapter<T> est un modèle permettant d'obtenir les vues enfants à afficher
- getView(int i, View convertView, ViewGroup parent) permet d'obtenir la vue à afficher pour l'enfant #i (implantations par défaut utilisant un TextView)
-
Adapteurs pré-définis :
- ArrayAdapter<T> : stockage d'une liste d'éléments
- SimpleCursorAdapter : modèle adaptant les données d'un Cursor de BDD
ListView et GridView (AbsListView)
☞ ListView et GridView sont dépréciés : le nouveau composant RecyclerView les remplace en proposant plus de possibilités de personnalisation (séparateurs, défilement horizontal ou vertical, animations...) et potentiellement plus de performance si de nombreux items doivent être affichés. ⟶ [Page abordant l'usage de ``RecyclerView`` recyclerView]
- ListView : affichage d'une liste avec défilement vertical puisant ses éléments dans un ListAdapter (classe dérivée avec liste à deux niveaux : ExpandableListView)
-
GridView : affichage sur une grille avec défilement vertical
- Attributs intéressants : columnWidth, gravity (alignement de l'enfant dans la cellule), numColumns, stretchMode
- Possibilité d'agir sur les évènements de défilement : setOnScrollListener(ScrollListener l)
Liste scrollable de nombres de Fibonacci
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.coursand.fibolist; import java.util.ArrayList; import android.app.Activity; import android.os.Bundle; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.ListView; import fr.upem.coursand.R; /** * An example of use of a ListView with a lazy loading induced by scrolling. * * @author chilowi at u-pem.fr */ public class FiboList extends Activity { /** Numbers of fibo numbers computed in a batch */ public static final int BATCH_SIZE = 10; private ArrayList<Long> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FrameLayout l = new FrameLayout(this); setContentView(l); list = new ArrayList<>(); // Create an adapter backed on the list ListView listView = new ListView(this); l.addView(listView); final ArrayAdapter<Long> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list); listView.setAdapter(adapter); // Create a scroll listener to load the numbers as the user scroll listView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // If we have scroll to the end of the list if (firstVisibleItem + visibleItemCount == totalItemCount) { // We must add new Fibonacci numbers generateNumbers(BATCH_SIZE); adapter.notifyDataSetChanged(); // The model must inform the view of the change } } }); generateNumbers(BATCH_SIZE); } /** Generate a batch of fibo numbers to add in the list */ private void generateNumbers(int numberToAppend) { int size = list.size(); for (int i = 0; i < numberToAppend; i++) { long v = ((size > 0)?list.get(size-1):1) + ((size > 1)?list.get(size-2):1); list.add(v); size++; } } }

GridView des versions d'Android
- Objectif : afficher en grille les différentes versions d'Android
- Récupération des versions d'Android par introspection sur Build.VERSION_CODES (consultation des champs statiques)
-
Affichage de chaque version en utilisant un layout personnalisé :
- Création du layout dans un fichier XML (LinearLayout avec deux TextView)
- Chargement du layout XML avec le LayoutInflater en redéfinissant la méthode getView() de l'adapter placé sur la GridView
- Appels de setText pour définir le nom de la version et le numéro d'API
- Choix d'une couleur aléatoire pour le fond du LinearLayout (lorsque la cellule est rafraîchie la couleur peut changer !)
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.coursand.versiongrid; import android.app.Activity; import android.graphics.Color; import android.graphics.Typeface; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.util.Pair; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.GridView; import android.widget.TextView; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Random; import fr.upem.coursand.R; /** * An activity showing a grid of the different versions of Android known by the system. * A randomly chosen color is given to each version. * * @author chilowi at u-pem.fr */ public class AndroidVersionsActivity extends Activity { public static List<Pair<Integer, String>> getAndroidVersions() { try { List<Pair<Integer, String>> l = new ArrayList<>(); for (Field f : Build.VERSION_CODES.class.getFields()) l.add(Pair.create(f.getInt(null), f.getName())); return l; } catch (Exception e) { Log.e(AndroidVersionsActivity.class.getName(), "Exception encountered", e); return null; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_android_versions); GridView gv = findViewById(R.id.versionGridView); final List<Pair<Integer, String>> versions = getAndroidVersions(); if (versions != null) gv.setAdapter(new ArrayAdapter<Pair<Integer, String>>(this, android.R.layout.simple_list_item_1) { final Random r = new Random(); @Override public int getCount() { return versions.size(); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) convertView = getLayoutInflater().inflate(R.layout.version_cell_layout, null); // set the name of the version TextView nameView = convertView.findViewById(R.id.versionNameTextView); nameView.setText(versions.get(position).second); // set the api level of the version TextView apiView = convertView.findViewById(R.id.versionApiTextView); apiView.setText("" + versions.get(position).first); // randomize the background color of the view (choose RVB values between 128 and 255) convertView.setBackgroundColor(Color.argb(255, 128 + r.nextInt(128), 128 + r.nextInt(128), 128 + r.nextInt(128))); // if this version is the current installed version, put the textviews in bold if (Build.VERSION.SDK_INT == versions.get(position).first) { nameView.setTypeface(Typeface.DEFAULT_BOLD); apiView.setTypeface(Typeface.DEFAULT_BOLD); } return convertView; } }); } }

Spinner
- Champ de texte avec menu déroulant pour choix d'un item unique
- Éléments de choix fournis par un SpinnerAdapter (interface implantée par ArrayAdapter)
-
Réaction à l'élément choisi avec un onItemSelectedListener
- void onItemSelected(AdapterView<?> parent, View view, int pos, long id) appelé pour un élément choisi
- void onNothingSelected(AdapterView<?> parent) appelé si aucun élément choisi
Exemple : sélection d'un pays dans un Spinner et affichage de sa capitale
- Création du fichier texte countryCapitals.csv associant à chaque pays sa capitale (exemple de ligne : France,Paris)
- Placement du fichier dans res/raw/ (ressource)
- Ouverture et chargement du fichier et création d'une Map<String, String> ssociant pays à capitale
- Utilisation d'un SpinnerAdapter pour afficher les clés de la Map
- Installation d'un OnItemSelectedListener pour rafraîchir CapitalTextView à la sélection d'un pays dans le Spinner
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.coursand.capitals; import android.graphics.Color; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; import java.util.ArrayList; import java.util.Map; import java.util.Scanner; import java.util.TreeMap; import fr.upem.coursand.R; public class CapitalsActivity extends AppCompatActivity { /** Create a map associating each country to its capital city * The data are read from the res/raw/country_capitals.csvv file * Each line follows the format: country,capital * @return a map linking countries to capitals */ public Map<String, String> loadCountryCapitals() { Map<String, String> map = new TreeMap<>(); try (Scanner s = new Scanner(getResources().openRawResource(R.raw.country_capitals))) { while (s.hasNextLine()) { String line = s.nextLine().trim(); if (!line.isEmpty()) { String[] elements = line.split(","); try { map.put(elements[0], elements[1]); } catch (Exception e) { Log.w(getClass().getName(), "Cannot interpret line " + line); } } } } return map; } private Map<String, String> countryCapitals; private ArrayAdapter<String> adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_capitals); // load the data countryCapitals = loadCountryCapitals(); // create the array adapter for the spinner adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<>(countryCapitals.keySet())) { /** Method called to display the selected item */ @Override public View getView(int position, View convertView, ViewGroup parent) { View v = super.getView(position, convertView, parent); // we add a yellow background for the selected item v.setBackgroundColor(Color.YELLOW); return v; } /** Method called to display items from the list (non-selected) */ @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { View v = super.getDropDownView(position, convertView, parent); // we alternate white and gray backgrounds for the items of the list v.setBackgroundColor((position % 2 == 0)?Color.WHITE:Color.LTGRAY); return v; } }; // set the array adapter on the spinner Spinner spinner = findViewById(R.id.countrySpinner); spinner.setAdapter(adapter); // install the selected item listener on the spinner final TextView capitalView = findViewById(R.id.capitalView); spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { // display the capital in the TextView String capital = countryCapitals.get(adapter.getItem(position)); capitalView.setText(capital); } @Override public void onNothingSelected(AdapterView<?> parent) { capitalView.setText("No country selected"); } }); } }
ViewPager
- Maintient une liste de vues parcourable par un geste de swipe
- A éviter pour les écrans non-tactiles (TV connectée)
-
Les vues à afficher sont fournies par un PagerAdapter (ViewPager.setAdapter()) qui est responsable de l'ajout et de la suppression d'éléments dans le ViewPager :
- Object instantiateItem(ViewGroup parent, int position) : méthode appelée lorsque la vue doit être installée dans le ViewGroup parent (doit faire l'installation) ; retourne un objet qui est une clé représentant l'item
- void destroyItem(ViewGroup parent, int pos, Object o) : méthode appelée pour la destruction d'un item (désinstalle du ViewGroup)
- int getCount() : retourne le nombre d'items dans l'adapter
- boolean isViewFromObject(View view, Object o) : permet d'indiquer si la vue et l'objet passés en paramètre sont liés (l'objet représentant une clé pour la vue)
-
Version spécifique de PagerAdapter utilisant des fragments : FragmentPagerAdapter
- Fragment getItem(int pos) : méthode abstraite à redéfinir