Android >> Ejemplo de ListView en Android

entrada-listview

Una vista ListView visualiza una lista deslizable verticalmente de varios elementos, donde cada elemento puede definirse como un layout. Su utilización es algo compleja, pero muy potente.

A continuación voy a realizar un ejemplo usando un ListView, pero a diferencia de otros ejemplos que he encontrado en la red, donde solo se muestra una lista por pantalla, voy a mostrar por pantalla la vista ListView junto a otra vista, en este caso una vista WebView, de tal manera que nuestra aplicación quedará de la siguiente manera:

ejemplo-listview-android-2

Primero vamos a crear las vistas de nuestra aplicación. En este caso tendremos dos: la vista principal main.xml y la vista para cada uno de los elementos de la lista list_item.xml. En el layout main.xml definimos una vista ListView y una vista WebView. Le hemos asignado a cada uno de los dos elementos un peso mediante el atributo android:layout_weight, en ambos elementos el peso es el mismo para dividir en dos partes iguales la pantalla:

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" />

</LinearLayout>

Como hemos dicho, el layout list_item.xml es la vista que tendrá cada uno de los elemento de la lista. En esta ocasión, para cada elemento vamos a mostrar una imagen y un texto:

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeight" >

    <ImageView
        android:id="@+id/ivItem"
        android:layout_width="?android:attr/listPreferredItemHeight"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:contentDescription="@string/app_name" />

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@id/ivItem"
        android:singleLine="true"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textIsSelectable="false" />

</RelativeLayout>

Bien, una vez definidos nuestras vistas vamos a empezar a crear la lógica de la aplicación. Comenzamos creando la clase Item, donde definiremos el contenido de cada uno de los elementos de la lista. En este caso, cada Item va a almacenar información sobre películas. Además de la imagen y el titulo que vamos a mostrar en la vista, almacenaremos también una dirección de una página Web que contiene información sobre la película, pero que no se va a mostrar en la lista.

Item.java

package com.amatellanes.android.examples;

public class Item {

	private int image;
	private String title;
	private String url;

	public Item() {
		super();
	}

	public Item(int image, String title, String url) {
		super();
		this.image = image;
		this.title = title;
		this.url = url;
	}

	public int getImage() {
		return image;
	}

	public void setImage(int image) {
		this.image = image;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

}

El siguiente paso es crear el adaptador ItemAdapter para la lista. Dicho adaptador debe heredar de la clase BaseAdapter. Deberemos implementar obligatóriamente los siguientes métodos:

    • int getCount() — Devuelve el número de elementos de la lista.
    • Object getItem(int position) — Devuelve el elemento en una determinada posición de la lista.
    • long getItemId(int position) — Devuelve el identificador de fila de una determinada posición de la lista.
    • View getView(int position, View convertView, ViewGroup parent) — Este método ha de construir un nuevo objeto View con el layout correspondiente a la posición position y devolverlo. Opcionalmente, podemos partir de una vista base convertView para generar más rápido las vistas. El último parámetro corresponde al padre al que la vista va a ser añadida.

Además vamos a definir el siguiente constructor:

    • ItemAdapter(Context context, List items) — Recibe el contacto de la aplicación y la lista de elementos que se van a mostrar en la lista.

La clase ItemAdapter quedaría de la siguiente manera:

ItemAdapter.java

package com.amatellanes.android.examples;

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.ImageView;
import android.widget.TextView;

public class ItemAdapter extends BaseAdapter {

	private Context context;
	private List<Item> items;

	public ItemAdapter(Context context, List<Item> items) {
		this.context = context;
		this.items = items;
	}

	@Override
	public int getCount() {
		return this.items.size();
	}

	@Override
	public Object getItem(int position) {
		return this.items.get(position);
	}

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

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

		View rowView = convertView;

		if (convertView == null) {
			// Create a new view into the list.
			LayoutInflater inflater = (LayoutInflater) context
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			rowView = inflater.inflate(R.layout.list_item, parent, false);
		}

		// Set data into the view.
		ImageView ivItem = (ImageView) rowView.findViewById(R.id.ivItem);
		TextView tvTitle = (TextView) rowView.findViewById(R.id.tvTitle);

		Item item = this.items.get(position);
		tvTitle.setText(item.getTitle());
		ivItem.setImageResource(item.getImage());

		return rowView;
	}

}

En el método getView() lo primero que vamos a hacer es comprobar si la vista convertView puede ser reutilizada para no tener que inflarla nuevamente si no es necesario, de esta manera optimizaremos nuestra lista. Existen otras técnicas para mejorar el rendimiento de una vista ListView que podéis consultar aquí.

Por ultimo, definimos nuestra actividad principal. Para hacer la aplicación más “profesional”, vamos a mostrar un indicador de carga y un mensaje mientras se está cargando la página Web con la información de la película. Para habilitar el indicador de carga en la barra de título debemos de escribir:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

	// Display a indeterminate progress bar on title bar
	requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

	this.setContentView(R.layout.main);

	...
}

Para mostrar y ocultar el indicador usamos el método setProgressBarIndeterminateVisibility con los valores true o false.

A continuación creamos la lista con los ítems (películas que se van a añadir a la lista) y llamar al método setListAdapter() para enviar a la lista de elementos a visualizar.

Luego debemos de definir la acción que se va a realizar cuando se seleccione cada uno de los elementos de la lista:

// Register a callback to be invoked when an item in this AdapterView
// has been clicked
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
	@Override
	public void onItemClick(AdapterView adapter, View view, int position, long arg) {

		// Sets the visibility of the indeterminate progress bar in the
		// title
		setProgressBarIndeterminateVisibility(true);

		// Show progress dialog
		progressDialog = ProgressDialog.show(MainActivity.this, "ProgressDialog", "Loading!");

		// Tells JavaScript to open windows automatically.
		webView.getSettings().setJavaScriptEnabled(true);

		// Sets our custom WebViewClient.
		webView.setWebViewClient(new myWebClient());

		// Loads the given URL
		Item item = (Item) listView.getAdapter().getItem(position);
			webView.loadUrl(item.getUrl());
	}
});

Como vemos cada vez que se pulse un elemento se mostrará el indicador de carga en la barra de título y el mensaje de carga, y a continuación se abrirá la dirección Web correspondiente a cada elemento en la lista.
Debemos de definir un cliente para que la página se cargue en la misma pantalla y nos se abra la página en un navegador Web que tengamos instalados, para ello definimos la siguiente clase interna:

private class myWebClient extends WebViewClient {

	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String url) {

		// Load the given URL on our WebView.
		view.loadUrl(url);
		return true;
	}

	@Override
	public void onPageFinished(WebView view, String url) {

		// When the page has finished loading, hide progress dialog and
		// progress bar in the title.
		super.onPageFinished(view, url);
		setProgressBarIndeterminateVisibility(false);
		progressDialog.dismiss();
	}
}

Hemos sobrescrito el método shouldOverrideUrlLoading para evitar que se abra la dirección Web en el navegador y además hemos sobrescrito el método onPageFinished para que se cierre el mensaje de carga y se oculta el indicador de la barra de título cuando se termine de cargar la la página Web. La clase MainActivity.java quedaría así:

MainActivity.java

package com.amatellanes.android.examples;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.AdapterView;
import android.widget.ListView;

public class MainActivity extends Activity {

	private ListView listView;
	private WebView webView;
	private ProgressDialog progressDialog;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Display a indeterminate progress bar on title bar
		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

		this.setContentView(R.layout.main);

		this.listView = (ListView) findViewById(R.id.listView);
		this.webView = (WebView) findViewById(R.id.webView);

		List items = new ArrayList();
		items.add(new Item(R.drawable.following, "Following",
				"http://www.imdb.com/title/tt0154506/"));
		items.add(new Item(R.drawable.memento, "Memento",
				"http://www.imdb.com/title/tt0209144/"));
		items.add(new Item(R.drawable.batman_begins, "Batman Begins",
				"http://www.imdb.com/title/tt0372784/"));
		items.add(new Item(R.drawable.the_prestige, "The Prestige",
				"http://www.imdb.com/title/tt0482571/"));
		items.add(new Item(R.drawable.the_dark_knight, "The Dark Knight",
				"http://www.imdb.com/title/tt0468569/"));
		items.add(new Item(R.drawable.inception, "Inception",
				"http://www.imdb.com/title/tt1375666/"));
		items.add(new Item(R.drawable.the_dark_knight_rises,
				"The Dark Knight Rises", "http://www.imdb.com/title/tt1345836/"));

		// Sets the data behind this ListView
		this.listView.setAdapter(new ItemAdapter(this, items));

		// Register a callback to be invoked when an item in this AdapterView
		// has been clicked
		listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> adapter, View view,
					int position, long arg) {

				// Sets the visibility of the indeterminate progress bar in the
				// title
				setProgressBarIndeterminateVisibility(true);
				// Show progress dialog
				progressDialog = ProgressDialog.show(MainActivity.this,
						"ProgressDialog", "Loading!");

				// Tells JavaScript to open windows automatically.
				webView.getSettings().setJavaScriptEnabled(true);
				// Sets our custom WebViewClient.
				webView.setWebViewClient(new myWebClient());
				// Loads the given URL
				Item item = (Item) listView.getAdapter().getItem(position);
				webView.loadUrl(item.getUrl());
			}
		});

	}

	private class myWebClient extends WebViewClient {

		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			// Load the given URL on our WebView.
			view.loadUrl(url);
			return true;
		}

		@Override
		public void onPageFinished(WebView view, String url) {

			// When the page has finished loading, hide progress dialog and
			// progress bar in the title.
			super.onPageFinished(view, url);
			setProgressBarIndeterminateVisibility(false);
			progressDialog.dismiss();
		}
	}
}

Por último vamos a modificar el fichero AndroidManifest.xml de nuestra aplicación. Definimos la actividad MainActivity como actividad principal. Para mejorar la presentación de la aplicación vamos a obligar que siempre se presente la vista en horizontal (landscape), para ello hemos añadido el siguiente atributo:

<activity android:screenOrientation="landscape" />
 

Además debemos añadir el permiso para permitir que nuestra aplicación pueda acceder a Internet:

<uses-permission android:name="android.permission.INTERNET" />

Nuestro AndroidManifest.xml quedaría tal que así:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.amatellanes.android.examples"
    android:versionCode="2"
    android:versionName="1.1" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.amatellanes.android.examples.MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="landscape" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

El resultado final de la aplicación será el siguiente:
ejemplo-listview-android-0

ejemplo-listview-android-1

ejemplo-listview-android-2

Descargar código | GitHub

Etiquetado , ,

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: