Android ListView con diferentes diseños para cada fila

Estoy tratando de determinar la mejor manera de tener un solo ListView que contenga diferentes diseños para cada fila. Sé cómo crear una fila personalizada + adaptador de matriz personalizado para admitir una fila personalizada para toda la vista de lista, pero ¿cómo puedo implementar muchos estilos de fila diferentes en ListView?

Como sabe cuántos tipos de diseño tendría, es posible usar esos métodos.

getViewTypeCount() – este método devuelve información sobre cuántos tipos de filas tiene en su lista

getItemViewType(int position) – devuelve información que tipo de diseño debe usar en función de la posición

Luego, infle el diseño solo si es nulo y determine el tipo usando getItemViewType .

Mira este tutorial para más información.

Para lograr algunas optimizaciones en la estructura que ha descrito en el comentario, sugeriría:

  • Almacenamiento de vistas en el objeto llamado ViewHolder . Aumentará la velocidad porque no tendrá que llamar a findViewById() cada vez en el método getView . Ver List14 en demostraciones de API .
  • Cree un diseño genérico que conformará todas las combinaciones de propiedades y ocultará algunos elementos si la posición actual no lo tiene.

Espero que eso te ayude. Si pudiera proporcionar algún fragmento XML con su estructura de datos e información sobre cómo exactamente desea mapearlo en fila, podría darle consejos más precisos. Por píxel.

Sé cómo crear una fila personalizada + adaptador de matriz personalizado para admitir una fila personalizada para toda la vista de lista. Pero, ¿cómo puede una vista de lista admitir muchos estilos de filas diferentes?

Ya sabes lo básico. Solo necesita obtener su adaptador personalizado para devolver un diseño / vista diferente según la información de fila / cursor que se proporciona.

Un ListView puede soportar múltiples estilos de filas porque deriva de AdapterView :

Un AdapterView es una vista cuyos hijos están determinados por un adaptador.

Si mira el Adaptador , verá los métodos que representan el uso de vistas específicas de la fila:

 abstract int getViewTypeCount() // Returns the number of types of Views that will be created ... abstract int getItemViewType(int position) // Get the type of View that will be created ... abstract View getView(int position, View convertView, ViewGroup parent) // Get a View that displays the data ... 

Los últimos dos métodos proporcionan la posición para que pueda usar eso para determinar el tipo de vista que debe usar para esa fila .


Por supuesto, generalmente no usa AdapterView y Adapter directamente, sino que usa o deriva de una de sus subclases. Las subclases de Adapter pueden agregar funcionalidad adicional que modifique la forma de obtener diseños personalizados para diferentes filas. Dado que la vista utilizada para una fila dada es impulsada por el adaptador, el truco consiste en hacer que el adaptador devuelva la vista deseada para una fila determinada. Cómo hacer esto difiere dependiendo del adaptador específico.

Por ejemplo, para usar ArrayAdapter ,

  • anula getView() para inflar, completar y devolver la vista deseada para la posición dada. El método getView() incluye una oportunidad de reutilizar vistas a través del parámetro convertView .

Pero para usar derivados de CursorAdapter ,

  • anula newView() para inflar, rellenar y devolver la vista deseada para el estado actual del cursor (es decir, la “fila” actual) [también necesita sobreescribir bindView para que el widget pueda reutilizar las vistas]

Sin embargo, para usar SimpleCursorAdapter ,

  • define un SimpleCursorAdapter.ViewBinder con un método setViewValue() para inflar, completar y devolver la vista deseada para una fila determinada (estado actual del cursor) y “columna” de datos. El método puede definir solo las vistas “especiales” y diferir el comportamiento estándar de SimpleCursorAdapter para las vinculaciones “normales”.

Busca los ejemplos / tutoriales específicos para el tipo de adaptador que terminas usando.

Eche un vistazo al código a continuación.

Primero, creamos diseños personalizados. En este caso, cuatro tipos.

even.xml

    

odd.xml

    

white.xml

    

black.xml

    

Entonces, creamos el elemento listview. En nuestro caso, con una cadena y un tipo.

 public class ListViewItem { private String text; private int type; public ListViewItem(String text, int type) { this.text = text; this.type = type; } public String getText() { return text; } public void setText(String text) { this.text = text; } public int getType() { return type; } public void setType(int type) { this.type = type; } } 

Después de eso, creamos un titular de vista. Se recomienda encarecidamente porque el sistema operativo Android mantiene la referencia de diseño para reutilizar el elemento cuando desaparece y vuelve a aparecer en la pantalla. Si no usa este enfoque, cada vez que su artículo aparezca en la pantalla, el sistema operativo Android creará uno nuevo y hará que su aplicación pierda memoria.

 public class ViewHolder { TextView text; public ViewHolder(TextView text) { this.text = text; } public TextView getText() { return text; } public void setText(TextView text) { this.text = text; } } 

Finalmente, creamos nuestro adaptador personalizado anulando getViewTypeCount () y getItemViewType (int position).

 public class CustomAdapter extends ArrayAdapter { public static final int TYPE_ODD = 0; public static final int TYPE_EVEN = 1; public static final int TYPE_WHITE = 2; public static final int TYPE_BLACK = 3; private ListViewItem[] objects; @Override public int getViewTypeCount() { return 4; } @Override public int getItemViewType(int position) { return objects[position].getType(); } public CustomAdapter(Context context, int resource, ListViewItem[] objects) { super(context, resource, objects); this.objects = objects; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder = null; ListViewItem listViewItem = objects[position]; int listViewItemType = getItemViewType(position); if (convertView == null) { if (listViewItemType == TYPE_EVEN) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_even, null); } else if (listViewItemType == TYPE_ODD) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_odd, null); } else if (listViewItemType == TYPE_WHITE) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_white, null); } else { convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_black, null); } TextView textView = (TextView) convertView.findViewById(R.id.text); viewHolder = new ViewHolder(textView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.getText().setText(listViewItem.getText()); return convertView; } } 

Y nuestra actividad es algo como esto:

 private ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // here, you can create a single layout with a listview listView = (ListView) findViewById(R.id.listview); final ListViewItem[] items = new ListViewItem[40]; for (int i = 0; i < items.length; i++) { if (i == 4) { items[i] = new ListViewItem("White " + i, CustomAdapter.TYPE_WHITE); } else if (i == 9) { items[i] = new ListViewItem("Black " + i, CustomAdapter.TYPE_BLACK); } else if (i % 2 == 0) { items[i] = new ListViewItem("EVEN " + i, CustomAdapter.TYPE_EVEN); } else { items[i] = new ListViewItem("ODD " + i, CustomAdapter.TYPE_ODD); } } CustomAdapter customAdapter = new CustomAdapter(this, R.id.text, items); listView.setAdapter(customAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { Toast.makeText(getBaseContext(), items[i].getText(), Toast.LENGTH_SHORT).show(); } }); } } 

ahora crea una vista de lista dentro de mainactivity.xml como esta

         

En su adaptador de matriz personalizado, anula el método getView (), como presuntamente familiar. Entonces, todo lo que tiene que hacer es usar una instrucción switch o una instrucción if para devolver una cierta vista personalizada dependiendo del argumento de posición que se pase al método getView. Android es inteligente ya que solo le dará una vista convertida del tipo apropiado para su posición / fila; no necesita verificar que sea del tipo correcto. Puede ayudar a Android con esto anulando apropiadamente los métodos getItemViewType () y getViewTypeCount ().

Si necesitamos mostrar diferentes tipos de vista en la vista de lista, entonces es bueno usar getViewTypeCount () y getItemViewType () en el adaptador en lugar de alternar una vista VIEW.GONE y VIEW.VISIBLE puede ser una tarea muy costosa dentro de getView () que afectar la lista de desplazamiento.

Verifique este para el uso de getViewTypeCount () y getItemViewType () en el Adaptador.

Enlace: the-use-of-getviewtypecount

ListView fue pensado para casos de uso simple como la misma vista estática para todos los artículos de la fila.
Ya que tiene que crear ViewHolders y hacer un uso significativo de getItemViewType() , y mostrar dinámicamente diferentes xml de layout de elementos de fila, debe intentar hacerlo usando RecyclerView , que está disponible en Android API 22. Ofrece mejor soporte y estructura para múltiples vistas tipos.

Consulte este tutorial sobre cómo usar RecyclerView para hacer lo que está buscando.