2011-01-23 12 views
318

Estoy tratando de determinar la mejor manera de tener un solo ListView que contenga diseños diferentes 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?Android ListView con diferentes diseños para cada fila

+1

: Demo para el diseño de múltiples filas de usar Android RecyclerView http : //code2concept.blogspot.in/2015/10/android-multiple-row-layout-using.html – nitesh

Respuesta

399

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

getViewTypeCount() - esto métodos devuelve información de cuántos tipos de filas tiene usted en su lista de

getItemViewType(int position) - devuelve información de qué tipo de diseño se debe utilizar en función de la posición

Luego se infla el diseño sólo si es nula y determine el tipo usando getItemViewType.

Consulte this tutorial para obtener más información.

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

  • Almacenamiento de vistas de objetos llamados ViewHolder. Aumentará la velocidad porque no tendrá que llamar al findViewById() cada vez en el método getView. Ver List14 in API demos.
  • Crea 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 te ayuden. 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.

+0

Gracias, el blog fue muy bueno, pero he agregado checkbox. Tuve un problema porque verificará el primer elemento y desplazará la Lista. Elementos extrañamente anónimos donde se verifican. ¿Puedes proporcionar una solución para eso? Gracias –

+2

perdón por excavar esto nuevamente, pero en realidad recomendaría tener un único archivo de diseño grande y controlar la visibilidad de partes de él, en lugar de tener archivos de diseño separados, que se inflan respectivamente usando getItemViewType? – Makibo

+2

Usted puede hacer eso también. Aunque todavía prefiero el camino expuesto aquí. Hace más claro lo que quieres lograr. – Cristian

12

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().

60

sé cómo crear un adaptador fila personalizada + matriz personalizada para soportar una fila de encargo 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 fila porque deriva de AdapterView:

Un AdapterView es una vista cuyos hijos son determinados por un adaptador.

Si nos fijamos en la Adapter, verá métodos que dan cuenta de uso de vistas fila específica:

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 proporcionar la posición para que pueda use eso para determine el tipo de vista debe usar para esa fila.


Por supuesto, por lo general no utilizan AdapterView y el adaptador directamente, sino que utiliza o se derivan 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 varía dependiendo del adaptador específico.

Por ejemplo, para utilizar ArrayAdapter,

  • override getView() para inflar, llenar, y volver 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 al utilizar derivados de CursorAdapter,

  • anulación newView() para inflar, llenar y devolver la vista deseada para el estado actual del cursor (es decir, la corriente "fila") [se también necesitará anular bindView para que widget se puede reutilizar vistas]

Sin embargo, para utilizar SimpleCursorAdapter,

  • definen un SimpleCursorAdapter.ViewBinder con un método de setViewValue() para inflar, llenar, y volver la vista deseada para una fila dada (estado actual del cursor) y "columna" datos. El método puede definir solo las vistas "especiales" y diferir el comportamiento estándar de SimpleCursorAdapter para las vinculaciones "normales".

Busque ejemplos/tutoriales específicos para el tipo de adaptador que acaba utilizando.

+0

¿Alguna idea de cuál de estos tipos de adaptadores es mejor para una implementación flexible del adaptador? Estoy agregando otra pregunta en la pizarra para esto. – Androider

+1

@Androider - "lo mejor para flexible" es muy abierto: no hay una clase final que satisfaga todas las necesidades; es una jerarquía rica: se reduce a si hay una funcionalidad en una subclase que sea útil para su propósito. Si es así, comience con esa subclase; de lo contrario, suba a 'BaseAdapter'. Derivar de BaseAdapter sería el más "flexible", pero sería el peor en la reutilización y madurez del código, ya que no aprovecha el conocimiento y la madurez ya puestos en los otros adaptadores. 'BaseAdapter' está ahí para contextos no estándar donde los otros adaptadores no encajan. –

+3

+1 para la distinción fina entre 'CursorAdapter' y' SimpleCursorAdapter'. –

3

Si tenemos que mostrar diferentes tipos de vista en vista de lista a continuación, es bueno utilizar getViewTypeCount() y getItemViewType() en el adaptador en lugar de alternar una vista VIEW.GONE y VIEW.VISIBLE puede ser una tarea muy cara interior getView() que afectará la lista de desplazamiento.

Compruebe este para el uso de getViewTypeCount() y getItemViewType() en el adaptador.

Enlace: the-use-of-getviewtypecount

1

ListView se aplica a los casos de uso simples como la misma visión estática de todos los elementos de las filas.
Dado que tiene que crear ViewHolders y hacer un uso significativo de getItemViewType(), y mostrar dinámicamente diferentes xml de diseño de elementos de fila, debe intentar hacerlo usando el RecyclerView, que está disponible en Android API 22. Ofrece mejor soporte y estructura para vistas múltiples tipos.

Echa un vistazo a este tutorial sobre cómo usar el RecyclerView para hacer lo que estás buscando.

35

Eche un vistazo en el siguiente código.

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

even.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ff500000" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/white" 
     android:layout_width="match_parent" 
     android:layout_gravity="center" 
     android:textSize="24sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

odd.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ff001f50" 
    android:gravity="right" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/white" 
     android:layout_width="wrap_content" 
     android:layout_gravity="center" 
     android:textSize="28sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

white.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ffffffff" 
    android:gravity="right" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/black" 
     android:layout_width="wrap_content" 
     android:layout_gravity="center" 
     android:textSize="28sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

black.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ff000000" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/white" 
     android:layout_width="wrap_content" 
     android:layout_gravity="center" 
     android:textSize="33sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

Luego, 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; 
     } 

    } 

Por último, creamos nuestra adaptador personalizado anulando getViewTypeCount() y getItemViewType (posición int).

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(); 
      } 
     }); 

    } 
} 

crear ahora una vista de lista en el interior mainactivity.xml como esta actualización

<?xml version="1.0" encoding="utf-8"?> 
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" 
    tools:context="com.example.shivnandan.gygy.MainActivity"> 

    <android.support.design.widget.AppBarLayout 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:theme="@style/AppTheme.AppBarOverlay"> 

     <android.support.v7.widget.Toolbar 
      android:id="@+id/toolbar" 
      android:layout_width="match_parent" 
      android:layout_height="?attr/actionBarSize" 
      android:background="?attr/colorPrimary" 
      app:popupTheme="@style/AppTheme.PopupOverlay" /> 

    </android.support.design.widget.AppBarLayout> 

    <include layout="@layout/content_main" /> 

    <ListView 
     android:layout_width="match_parent" 

     android:layout_height="match_parent" 

     android:id="@+id/listView" 
     android:layout_alignParentRight="true" 
     android:layout_alignParentEnd="true" 


     android:layout_marginTop="100dp" /> 

</android.support.design.widget.CoordinatorLayout> 
+0

Gracias, funciona. – oguzhan

+0

de dónde viene eso – nyxee

Cuestiones relacionadas