Android – ListView avec plusieurs types de vues – Mise en place du recyclage

Cet article présente un exemple permettant d’afficher différents types de vues au sein d’une même liste (ListView). L’intérêt de cet article est de présenter comment gérer cela de manière efficace comme l’Efficient Adapter proposé dans l’APIDemos permettant d’optimiser les ressources. Voici ce que cela peut donner dans Scores Keeper pour afficher le détail d’un participant :

Tout d’abord voici un activité très simple ayant un ListView auquel on lui donne un adapter. Rien de bien compliqué 🙂 :

package com.blogdebenoit.multiviewlist;

import android.app.ListActivity;
import android.os.Bundle;

public class MultiViewListActivity extends ListActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getListView().setAdapter(new MultiViewAdapter(this));
    }
}

Et voilà la partie interressante de l’article, l’adapter qui va nous générer les vues pour la liste. Celui-ci est capable d’utiliser le système de recyclage des vues de l’Efficient Adapter. Pour bien mettre en évidence l’utilisation de différentes vues, cet adapter va gérer 2 listes de données (une de fruits et l’autre de légumes). Il va séparer ces 2 listes par des en-têtes et nous allons afficher tout ça dans une seule et même liste pour obtenir ce résultat :

Commençons par un BaseAdapter classique. Celui-ci dispose donc de 2 tableaux de String : fruits et vegetables.

  • La méthode getCount retourne le nombre de fruits plus le nombre de légume plus les 2 en-têtes de chaque liste.
  • La méthode getItem va aller chercher le bon nom à retourner suivant que l’on souhaite afficher un fruit, un légume ou un entête :
public class MultiViewAdapter extends BaseAdapter {

	// data
	private String[] fruits = new String[] { "Abricot", "Banana", "Blueberry", "Mango", };
	private String[] vegetables = new String[] { "Aubergene", "Broccoli", "Chard" };

	private LayoutInflater inflater;

	public MultiViewAdapter(Context context) {
		super();
		this.inflater = LayoutInflater.from(context);
	}

	@Override
	public int getCount() {
		return fruits.length + vegetables.length + 2;
	}

	@Override
	public Object getItem(int position) {
		if (position == 0) {
			return "Fruits";
		} else if (position < (1 + fruits.length)) {
			return fruits[position - 1];
		} else if (position < (1 + fruits.length + 1)) {
			return "Vegetables";
		} else {
			return vegetables[position - 1 - fruits.length - 1];
		}
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		return null;
	}
}

Afin de pouvoir gérer différentes vues et de gérer leur recyclage nous avons besoin de deux méthodes de BaseAdapter qui sont facultatives par défaut getViewTypeCount et getItemViewType. La première précise le nombre de type de vues que l’on souhaite gérer dans cet adapter et la seconde permet d’identifier le type de vue que l’on souhaite afficher en fonction de sa position. Cet adapter va gérer 3 types de vues pour cet exemple les fruits, les légumes et les en-têtes :

	// available view types
	private static final int TYPE_HEADER = 0;
	private static final int TYPE_FRUIT = 1;
	private static final int TYPE_VEGETABLE = 2;

	@Override
	public int getViewTypeCount() {
		return 3;
	}

	/**
	 * Return the type depending on the position
	 */
	@Override
	public int getItemViewType(int position) {
		if ((position == 0) || (position == (1 + fruits.length))) {
			return TYPE_HEADER;
		} else if (position < (1 + fruits.length)) {
			return TYPE_FRUIT;
		} else {
			return TYPE_VEGETABLE;
		}
	}

Enfin voici la méthode getView. Elle a l’air asez compliquée mais ça n’est en fait que la méthode getView de l’Efficient adapter à laquelle j’ai rajouté différents cas suivant le type de vue que l’on doit créer/recycler. L’aspect très intéressant de ce système c’est que le BaseAdapter va recycler automatiquement les vues en nous fournissant une vue du bon type.

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
        // A ViewHolder keeps references to children views to avoid uneccessary calls
        // to findViewById() on each row.
		ViewHolder viewHolder;
		int ItemViewType = getItemViewType(position);

        // When convertView is not null, we can reuse it directly, there is no need
        // to reinflate it. We only inflate a new View when the convertView supplied
        // by ListView is null.
		if (convertView == null) {
			viewHolder = new ViewHolder();

            // Creates a ViewHolder and store references to the children views
            // we want to bind data to.
			if (ItemViewType == TYPE_HEADER) {
				// create a header view
				convertView = inflater.inflate(android.R.layout.simple_expandable_list_item_1, null);
				convertView.setBackgroundColor(Color.parseColor("#FFFFFF"));
				viewHolder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
				viewHolder.text1.setTextColor(Color.parseColor("#000000"));
			} else {
				// create a fruit/vegetable view
				convertView = inflater.inflate(android.R.layout.simple_expandable_list_item_2, null);
				viewHolder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
				viewHolder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
			}
			convertView.setTag(viewHolder);
		} else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
			viewHolder = (ViewHolder) convertView.getTag();
		}

        // Bind the data efficiently with the holder.
		String label = (String) getItem(position);
		viewHolder.text1.setText(label);
		if (ItemViewType == TYPE_FRUIT) {
			viewHolder.text2.setText("fruit");
		} else if (ItemViewType == TYPE_VEGETABLE) {
			viewHolder.text2.setText("vegetable");
		}

		return convertView;
	}

	static class ViewHolder {
		TextView text1;
		TextView text2;
	}

Pour finir voici le code source de l’adapter complet :

package com.blogdebenoit.multiviewlist;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class MultiViewAdapter extends BaseAdapter {

	// available view types
	private static final int TYPE_HEADER = 0;
	private static final int TYPE_FRUIT = 1;
	private static final int TYPE_VEGETABLE = 2;

	// data
	private String[] fruits = new String[] { "Abricot", "Banana", "Blueberry", "Mango", };
	private String[] vegetables = new String[] { "Aubergene", "Broccoli", "Chard" };

	private LayoutInflater inflater;

	public MultiViewAdapter(Context context) {
		super();
		this.inflater = LayoutInflater.from(context);
	}

	@Override
	public int getCount() {
		return fruits.length + vegetables.length + 2;
	}

	@Override
	public Object getItem(int position) {
		if (position == 0) {
			return "Fruits";
		} else if (position < (1 + fruits.length)) {
			return fruits[position - 1];
		} else if (position < (1 + fruits.length + 1)) {
			return "Vegetables";
		} else {
			return vegetables[position - 1 - fruits.length - 1];
		}
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public int getViewTypeCount() {
		return 3;
	}

	/**
	 * Return the type depending on the position
	 */
	@Override
	public int getItemViewType(int position) {
		if ((position == 0) || (position == (1 + fruits.length))) {
			return TYPE_HEADER;
		} else if (position < (1 + fruits.length)) {
			return TYPE_FRUIT;
		} else {
			return TYPE_VEGETABLE;
		}
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
        // A ViewHolder keeps references to children views to avoid uneccessary calls
        // to findViewById() on each row.
		ViewHolder viewHolder;
		int ItemViewType = getItemViewType(position);

        // When convertView is not null, we can reuse it directly, there is no need
        // to reinflate it. We only inflate a new View when the convertView supplied
        // by ListView is null.
		if (convertView == null) {
			viewHolder = new ViewHolder();

            // Creates a ViewHolder and store references to the children views
            // we want to bind data to.
			if (ItemViewType == TYPE_HEADER) {
				// create a header view
				convertView = inflater.inflate(android.R.layout.simple_expandable_list_item_1, null);
				convertView.setBackgroundColor(Color.parseColor("#FFFFFF"));
				viewHolder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
				viewHolder.text1.setTextColor(Color.parseColor("#000000"));
			} else {
				// create a fruit/vegetable view
				convertView = inflater.inflate(android.R.layout.simple_expandable_list_item_2, null);
				viewHolder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
				viewHolder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
			}
			convertView.setTag(viewHolder);
		} else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
			viewHolder = (ViewHolder) convertView.getTag();
		}

        // Bind the data efficiently with the holder.
		String label = (String) getItem(position);
		viewHolder.text1.setText(label);
		if (ItemViewType == TYPE_FRUIT) {
			viewHolder.text2.setText("fruit");
		} else if (ItemViewType == TYPE_VEGETABLE) {
			viewHolder.text2.setText("vegetable");
		}

		return convertView;
	}

	static class ViewHolder {
		TextView text1;
		TextView text2;
	}
}

J’espère que cet article est assez clair. N’hésitez pas à poser vos questions si besoin.

Publicités

Étiquettes : , , , , ,

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :