Hola de nuevo. Ahora quiero hablaros de un pequeño truco que he descubierto. La aplicación Android de MasQueCursos.org utiliza varios web services para leer los datos de Internet: lista de cursos, datos de un curso, lista de centros, datos de un centro, lista de categorías, etc. Para no sobrecargar las comunicaciones, en lugar de cargar todos los resultados a mostrar de golpe (por ejemplo cursos), lo hago en bloques de 25. Al principio puse botones para paginar igual que hay en la web: siguiente página, anterior página, primera página y última página, pero eso no quedaba demasiado bien en el móvil: los botones comen espacio para los resultados y la pantalla se veía demasiado llena. Así que decidí que lo ideal era cargar los 25 primeros resultados y cuando el usuario moviera la lista para ver el último, se cargaran automáticamente los siguientes 25 resultados.
Estuve investigando diferentes soluciones en Internet, pero ninguna me acababa de gustar. La mayoría usaban un evento mediante la función setOnScrollListener(), pero me dio algunos problemas y busqué otras soluciones. Al final resulta que ha sido relativamente fácil.
La solución consiste en añadir un elemento vacío al final de la lista de datos de forma que cuando Android llama a la función getView() del adaptador del ListView, si la llamada se hace para la última posición, cargamos los nuevos resultados. A continuación pongo el código de la clase ListViewActivity, que es la que se encarga de gestionar la lista de resultados. Atención seguir este código requiere un mínimo de conocimientos de programación Android.
public class ListViewActivity extends Activity implements OnItemClickListener { // Miembros de la clase private ListViewData[] mListViewData; // Controles del layout private ListView mListView; // Parámetros de llamada private int mStart; private int mLimit; // Tamaño de la vista private int mCount; private int mActual; @Override public void onCreate(Bundle savedInstanceState) { // Llamada a la clase padre super.onCreate(savedInstanceState); // Inicialización de la vista setContentView(R.layout.listviews); // Obtención de referencias permanentes a los controles mListView = (ListView) findViewById(R.id.listview); // Tratamiento de eventos del ListView mListView.setOnItemClickListener(this); // Inicializaciones mListViewData = null; mActual = 0; mCount = 0; // Se Obtención y carga de datos en el ListView mStart = 0; mLimit = 25; _loadDataCount(); _loadDataList(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // Validaciones iniciales (necesario al haber un item de más) if(mListViewData == null || mListViewData[position] == null) return; // Tratamiento de la pulsación sobre el item mListViewData[position] // ... } private void _loadDataCount() { // Aquí cargamos en mCount el tamaño total de los datos, ya sea mediante // una query a una base de datos interna o extarna o mediante una llamada // REST o lo que sea. // ... } private void _loadDataList() { // Aquí accedermos a la fuente de datos (base de datos, web service u // otro) para obtener los datos entre mStart y mLimit y los cargamos // por ejemplo en un array llamado data. // ... // Se cargan los datos en el ListView _fillListView(data); } private void _fillListView(ListViewData[] data) { // Se cuenta el número de items en el array de datos int dataCount = data.length(); // Establecimiento de los textos de la cabecera mActual += dataCount; // Si no hay datos es un error error if(dataCount < 1) { // Hacer el tratamiento del error // ... // Fin por error return; } // Nos guardamos el array de datos anterior ListViewData[] oldData = mListViewData; // Se crea un array de n + 1 items para el ListView excepto si ya se han // cargado todos los datos. Si hay un item de más, éste quedará vacío int count = mActual; if(mActual < mCount) ++count; mListViewData = new ListViewData[count]; // Si había un array anterior, se traspasa la información al nuevo int position = 0; if(oldData != null) { for(int i = 0; i < oldData.length; ++i) { if(oldData[i] != null) { mListViewData[position++] = oldData[i]; } } } // Traspaso de la información leída de la fuente de datos for (int i = 0; i < dataCount; i++) { mListViewData[position++] = new ListViewData(data[i]); } // Si hay más resultados, el último item irá vacío if(mActual < mCount) mListViewData[mActual] = null; // Se asigna el nuevo adaptador al ListView DataAdapter adapter = new DataAdapter(); mListView.setAdapter(adapter); // Scroll del ListView al último item leído mListView.setSelection((mStart > 0) ? mStart - 1 : 0); } public class ListViewData { // En esta clase se definen los datos y métodos para acceder a ellos. // ... } public class DataAdapter extends ArrayAdapter<ListViewData> { public DataAdapter() { // Llamada a la clase padre super(ListViewActivity.this, R.layout.listitem, mListViewData); } public View getView(int position, View convertView, ViewGroup parent) { // Obtención del objeto que se usa para cargar los items LayoutInflater inflater = ListViewActivity.this.getLayoutInflater(); View item; if(position == mActual) { // Inicializaciones item = inflater.inflate(R.layout.listitem, null); // Traspaso de información al item recién creado // ... // Se carga una nueva lista de cursos mStart = mActual; mLimit = 25; _loadDataList(); } else { // Inicializaciones item = inflater.inflate(R.layout.listitem, null); // Traspaso de información al item recién creado // ... } // Finalización return(item); } } }
PD: ¿Alguien sabe como hacer que el código fuente salga coloreado en el blog? Con las palabras clave resaltadas y eso.