Android : Afficher des données avec les adapter

20 octobre 2012 rdorigny 0 commentaires

Le framework Android propose des facilités pour afficher vos données, cet artifice est possible grâce aux adapter. Ce sont des mécanismes préparés qui adaptent les données pour affichage choisis parmi plusieurs modèles.

Les adapters permettent en quelques lignes de code d'afficher une liste ou une grille de données. Ils ont un effet simplificateur pour le code et permet de rendre générique certaines actions courantes. Nous verrons qu'il est possible de personnaliser les éléments qui seront affichés.

Le passage par un adapter est très courant en code JAVA pour Android, et donc nous allons étudier ce mécanisme en détails dans ce chapitre.

1)Généralités

Les adapters transmettent les données aux AdapterView. La classe AdapterView définit des sous-classes qui présentent les vues View qui sont les éléments de la sous-classe.

AdapterView

classe Adapter

Description

ListView ListAdapter Affiche une liste des données vertical
GridView ListAdapter Affiche un tableau de données
Spinner Adapter Affiche une liste des données déroulante
Gallery Adapter Affiche une liste horizontale d'images


Rappelons que l'objectif des adapters et de simplifier le code à produire pour afficher un rendu géré par le framework Android.

2)Cas du ListView

Il y a deux cas, soit on utilise une ListActivity soit non.

2.1)Exemple d'utilisation d'une ListActivity

La classe ListActivity prédéfinit une liste ListView qui doit impérativement avoir un id dénommé list (sinon vous aurez une erreur).
<?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" > <ListView android:id="@android:id/list" android:layout_width="wrap_content" android:layout_height="wrap_content"> </ListView> </LinearLayout>


package com.example.test; import android.os.Bundle; import android.app.ListActivity; import android.widget.ArrayAdapter; public class MainActivity extends ListActivity { private String[] mStrings = { "un", "deux", "trois", "quatre", "cinq" }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mStrings); setListAdapter(adapter); } }

Ce qui donne:


La classe ListActivity décline une méthode pour les événements onClick.

@Override protected void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); Toast toast=Toast.makeText(this,mStrings[position],Toast.LENGTH_LONG); toast.show(); }

2.2)Créer une ListView sansListActivity

Dans l'exemple précédent, un certains nombre d'actions étaient prédéfinies, c'est à dire réalisées automatiquement. Voici une autre méthode pour faire un ListView.

<?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"> <ListView android:id="@+id/ListView01" android:layout_width="wrap_content" android:layout_height="wrap_content"> </ListView> </LinearLayout>

package com.example.test; import android.os.Bundle; import android.app.Activity; import android.widget.ArrayAdapter; import android.widget.ListView; public class MainActivity extends Activity { private String[] mStrings = {"un", "deux", "trois", "quatre", "cinq"}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Création de l'adapter ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mStrings); //Récupération du ListView présent dans notre IHM ListView list = (ListView)findViewById(R.id.ListView01); //On passe nos données au composant ListView list.setAdapter(adapter); } }

3)Personnaliser un adaptateur

Il existe des cas pour lesquels il n'existe pas d'adaptateur pour ce que l'on veut faire. Il est faudra donc créer un adapter personnalisé avec toutes les méthodes associées de la classe BaseAdapter. Pour créer un adapter personnalisé, il faut le faire hériter de la classe BaseAdapter et surcharger plusieurs méthodes:
  • getCount(): nombre des item à afficher,
  • getItem(): renvoit la donnée selon la position dans la collection,
  • getItemId(): permet de trier les données,
  • getView(): renvoit la vue adatée aux données.

  • Nous allons réaliser un exemple sur la base d'une application qui liste les packages installés sur l'OS Android.

    3.1)Etape 1: Afficher les applications de la station Android

    Nous allons créer une classe App pour représenter une application, et créer une List JAVA d'App qui représentera les données à adapter.
    package com.example.test; public class App { private String title; private String packageName; private String versionName; private int versionCode; //getters et setters public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public String getVersionName() { return versionName; } public void setVersionName(String versionName) { this.versionName = versionName; } public int getVersionCode() { return versionCode; } public void setVersionCode(int versionCode) { this.versionCode = versionCode; } }

    Le layout principal définit une ListView pour afficher les applications.
    <?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"> <ListView android:id="@+id/applist" android:layout_width="wrap_content" android:layout_height="wrap_content"> </ListView> </LinearLayout>

    Ensuite, on définit la vue de l'item row.xml avec un TextView pour afficher le nom de l'application.
    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <TextView android:id="@+id/apptitle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="20dip" android:layout_gravity="center_vertical" /> </LinearLayout>

    Ensuite on définit une classe d'adapter AppListAdapter qui fournit les données dans les vues d'item row.xml. Pour cela on surcharge les méthodes du BaseAdapter nécessaire au fonctionnement du framework. La méthode getView() définit fait le lien entre les données et la vue row. A noter, la classe AppViewHolder permet de ne pas recalculer le contenu du fichier xml.
    package com.example.test; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; public class AppListAdapter extends BaseAdapter { private LayoutInflater mInflater; private List<App> mApps; public int getCount() { return mApps.size(); } public AppListAdapter(Context context) { mInflater=LayoutInflater.from(context); } public Object getItem(int position) { return mApps.get(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { AppViewHolder holder; if (convertView==null){ convertView=mInflater.inflate(R.layout.row, null); holder=new AppViewHolder(); holder.mTitle=(TextView) convertView.findViewById(R.id.apptitle); convertView.setTag(holder); } else { holder=(AppViewHolder) convertView.getTag(); } holder.setTitle(mApps.get(position).getTitle()); return convertView; } public void setListItems(List<App> list){ mApps=list; } //Class pour réutiliser les différentes vues public class AppViewHolder { private TextView mTitle; public void setTitle(String title){ mTitle.setText(title); } } }

    Enfin, l'activité principale, avec la méthode loadInstalledApps qui initialise la liste d'applications à afficher.
    package com.example.test; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import android.widget.Toast; public class MainActivity extends Activity implements OnItemClickListener { private static final boolean INCLUDE_SYSTEM_APP=true; private ListView mAppsList; private List<App> mApps; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAppsList=(ListView) findViewById(R.id.applist); mAppsList.setOnItemClickListener(this); //Initialisation de la liste d'applications mApps=loadInstalledApps(INCLUDE_SYSTEM_APP); AppListAdapter adapter=new AppListAdapter(getApplicationContext()); adapter.setListItems(mApps); mAppsList.setAdapter(adapter); } public void onItemClick(AdapterView<?> p, View v, int position, long id) { App app=(App) p.getItemAtPosition(position); Intent i=getPackageManager().getLaunchIntentForPackage(app.getPackageName()); try { if (i!=null){ startActivity(i); } else { i=new Intent(app.getPackageName()); startActivity(i); } } catch (ActivityNotFoundException err) { Toast.makeText(MainActivity.this, "Erreur lors du lancement de l'application", Toast.LENGTH_SHORT).show(); } } private List<App> loadInstalledApps(boolean includeSysApps){ List<App> apps=new ArrayList<App>(); PackageManager packageManager=getPackageManager(); List<PackageInfo> pgs=packageManager.getInstalledPackages(0); for (int i=0;i<pgs.size();i++){ PackageInfo p=pgs.get(i); ApplicationInfo a=p.applicationInfo; if ((!includeSysApps)&&(a.flags & ApplicationInfo.FLAG_SYSTEM)==1){ continue; } App app=new App(); app.setTitle(p.applicationInfo.loadLabel(packageManager).toString()); app.setPackageName(p.packageName); app.setVersionName(p.versionName); app.setVersionCode(p.versionCode); apps.add(app); } return apps; } }

    3.2)Afficher les applications avec leurs icônes

    Pour cela on modifie le fichier row.xml en ajoutant l'image dans le layout.
    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <ImageView android:id="@+id/appicon" android:scaleType="fitCenter" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginRight="6dip" android:layout_marginLeft="2dip" android:layout_marginTop="2dip" android:src="@drawable/ic_launcher" android:layout_gravity="center_vertical" /> <TextView android:id="@+id/apptitle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="20dip" android:layout_gravity="center_vertical" /> </LinearLayout>


    package com.example.test; public class App { private String title; private String packageName; private String versionName; private int versionCode; private String description; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } //getters et setters public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public String getVersionName() { return versionName; } public void setVersionName(String versionName) { this.versionName = versionName; } public int getVersionCode() { return versionCode; } public void setVersionCode(int versionCode) { this.versionCode = versionCode; } }

    On modifie l'adapter pour prendre en compte l'image, on utilise une collection Map pour conserver les applications.
    package com.example.test; import java.util.List; import java.util.Map; import android.content.Context; import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; public class AppListAdapter extends BaseAdapter { private LayoutInflater mInflater; private List<App> mApps; //Map contenant le nom du package et le nom de son image private Map<String,Drawable> mIcons; //Image standard private Drawable mStdImg; public int getCount() { return mApps.size(); } public AppListAdapter(Context context) { mInflater=LayoutInflater.from(context); mStdImg=context.getResources().getDrawable(R.drawable.ic_launcher); } public Object getItem(int position) { return mApps.get(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { AppViewHolder holder; if (convertView==null){ convertView=mInflater.inflate(R.layout.row, null); holder=new AppViewHolder(); holder.mTitle=(TextView) convertView.findViewById(R.id.apptitle); holder.mIcon=(ImageView) convertView.findViewById(R.id.appicon); convertView.setTag(holder); } else { holder=(AppViewHolder) convertView.getTag(); } App app=mApps.get(position); holder.setTitle(app.getTitle()); if (mIcons==null || mIcons.get(app.getPackageName())==null){ holder.setIcon(mStdImg); } else { holder.setIcon(mIcons.get(app.getPackageName())); } return convertView; } public void setListItems(List<App> list){ mApps=list; } public void setIcons(Map<String,Drawable> icons){ this.mIcons=icons; } public Map<String,Drawable> getIcons(){ return mIcons; } //Class pour réutiliser les différentes vues public class AppViewHolder { private TextView mTitle; private ImageView mIcon; public void setTitle(String title){ mTitle.setText(title); } public void setIcon(Drawable img){ if (img!=null){ mIcon.setImageDrawable(img); } } } }

    Le chargement des fichiers icônes peut être assez long car il y a un accès au système de fichiers. Nous allons donc faire un chargement asynchrone grâce en faisant hériter la classe interne LoadIconsTas avec AsyncTask.
    package com.example.test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.os.AsyncTask; import android.os.Bundle; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import android.widget.Toast; public class MainActivity extends Activity implements OnItemClickListener { private static final boolean INCLUDE_SYSTEM_APP=true; private ListView mAppsList; private List<App> mApps; private AppListAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAppsList=(ListView) findViewById(R.id.applist); mAppsList.setOnItemClickListener(this); //Initialisation de la liste d'applications mApps=loadInstalledApps(INCLUDE_SYSTEM_APP); mAdapter=new AppListAdapter(getApplicationContext()); mAdapter.setListItems(mApps); mAppsList.setAdapter(mAdapter); new LoadIconsTask().execute(mApps.toArray(new App[]{})); } public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final App app = (App) parent.getItemAtPosition(position); AlertDialog.Builder builder = new AlertDialog.Builder(this); String msg = app.getTitle() + "nn" + "Version " + app.getVersionName() + " (" + app.getVersionCode() + ")" + (app.getDescription() != null ? ("nn" + app.getDescription()) : ""); builder.setMessage(msg) .setCancelable(true) .setTitle(app.getTitle()) .setIcon(mAdapter.getIcons().get(app.getPackageName())) .setPositiveButton("Launch", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // start the app by invoking its launch intent Intent i = getPackageManager().getLaunchIntentForPackage(app.getPackageName()); try { if (i != null) { startActivity(i); } else { i = new Intent(app.getPackageName()); startActivity(i); } } catch (ActivityNotFoundException err) { Toast.makeText(MainActivity.this, "Error launching app", Toast.LENGTH_SHORT).show(); } } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog dialog = builder.create(); dialog.show(); } private List<App> loadInstalledApps(boolean includeSysApps){ List<App> apps=new ArrayList<App>(); PackageManager packageManager=getPackageManager(); List<PackageInfo> pgs=packageManager.getInstalledPackages(0); for (int i=0;i<pgs.size();i++){ PackageInfo p=pgs.get(i); ApplicationInfo a=p.applicationInfo; if ((!includeSysApps)&&(a.flags & ApplicationInfo.FLAG_SYSTEM)==1){ continue; } App app=new App(); app.setTitle(p.applicationInfo.loadLabel(packageManager).toString()); app.setPackageName(p.packageName); app.setVersionName(p.versionName); app.setVersionCode(p.versionCode); apps.add(app); } return apps; } private class LoadIconsTask extends AsyncTask<App, Void, Void> { @Override protected Void doInBackground(App... apps) { Map<String, Drawable> icons = new HashMap<String, Drawable>(); PackageManager manager = getApplicationContext().getPackageManager(); for (App app : apps) { String pkgName = app.getPackageName(); Drawable ico = null; try { Intent i = manager.getLaunchIntentForPackage(pkgName); if (i != null) { ico = manager.getActivityIcon(i); } } catch (NameNotFoundException e) { Log.e("ERROR", "Unable to find icon for package '" + pkgName + "': " + e.getMessage()); } icons.put(app.getPackageName(), ico); } mAdapter.setIcons(icons); return null; } @Override protected void onPostExecute(Void result) { mAdapter.notifyDataSetChanged(); } }

    Ce qui donnera visuellement:

    4)Conclusion

    Comme nous le disions initialement, on trouve les adapter un peu partout dans les applications Android. Il en existe beaucoup de modèle, et probablement un correspond à ce que vous voulez faire.









    Pseudonyme (obligatoire) :
    Adresse mail (obligatoire) :
    Site web :




    © 2017 www.doritique.fr par Robert DORIGNY