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