con - listview dinamico android studio
ListView de Android con diferentes diseƱos para cada fila (6)
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 + un adaptador de matriz personalizado para admitir una fila personalizada para la vista de lista completa, pero ¿cómo puedo implementar muchos estilos de fila diferentes en el ListView?
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 fila 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 la fila / cursor que se proporciona.
Un ListView
puede admitir varios estilos de fila porque se deriva de AdapterView :
Un AdapterView es una vista cuyos hijos están determinados por un adaptador.
Si observa el Adapter , verá métodos que explican el uso de vistas específicas de 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 dos últimos métodos proporcionan la posición para que pueda usarla para determinar el tipo de vista que debe usar para esa fila .
Por supuesto, generalmente no usa AdapterView y Adapter directamente, sino que utiliza o deriva de una de sus subclases. Las subclases de Adaptador pueden agregar funcionalidad adicional que cambie la forma de obtener diseños personalizados para diferentes filas. Dado que la vista utilizada para una fila dada está controlada por el adaptador, el truco es conseguir que el adaptador devuelva la vista deseada para una fila dada. Cómo hacerlo difiere dependiendo del adaptador específico.
Por ejemplo, para usar ArrayAdapter ,
-
getView()
para inflar, rellenar y devolver la vista deseada para la posición dada. El métodogetView()
incluye una oportunidad de reutilización de vistas a través del parámetroconvertView
.
Pero para usar derivados de CursorAdapter ,
- anule
newView()
para inflar, rellenar y devolver la vista deseada para el estado actual del cursor (es decir, la "fila" actual) [también debe anularbindView
para que el widget pueda reutilizar las vistas]
Sin embargo, para usar SimpleCursorAdapter ,
- defina un
SimpleCursorAdapter.ViewBinder
con un métodosetViewValue()
para inflar, rellenar y devolver la vista deseada para una fila dada (estado actual del cursor) y datos "columna". El método puede definir solo las vistas "especiales" y diferir al comportamiento estándar de SimpleCursorAdapter para los enlaces "normales".
Busque los ejemplos / tutoriales específicos para el tipo de adaptador que termina usando.
Echa un vistazo en el código de abajo.
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>
blanco.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 su elemento cuando desaparece y aparece de nuevo en la pantalla. Si no utiliza este enfoque, cada vez que aparezca un elemento 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 (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 así:
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
<?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>
En su adaptador de matriz personalizado, anula el método getView (), como presumiblemente está familiarizado. Luego, todo lo que tiene que hacer es usar una instrucción de cambio o una instrucción if para devolver una determinada vista personalizada según el argumento de posición pasado al método getView. Android es inteligente en cuanto a que solo le dará un convertView del tipo apropiado para su posición / fila; No es necesario verificar que sea del tipo correcto. Puede ayudar a Android con esto anulando los métodos getItemViewType () y getViewTypeCount () adecuadamente.
ListView estaba destinado a casos de uso simples como la misma vista estática para todos los elementos de fila.
Ya que tiene que crear ViewHolders y hacer un uso significativo de getItemViewType()
, y mostrar dinámicamente diferentes xml de elementos de elementos de fila, debe intentar hacerlo utilizando RecyclerView , que está disponible en la API de Android 22. Ofrece mejor soporte y estructura para vistas múltiples. tipos
Echa un vistazo a este tutorial sobre cómo usar RecyclerView para hacer lo que estás buscando.
Si necesitamos mostrar diferentes tipos de vistas 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 a la lista de desplazamiento.
Por favor, marque este para el uso de getViewTypeCount () y getItemViewType () en el adaptador.
Enlace: the-use-of-getviewtypecount
Ya que 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 sobre qué tipo de diseño debe usar según la posición
Luego infla el diseño solo si es nulo y determina 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 un objeto llamado
ViewHolder
. Aumentaría la velocidad porque no tendrá que llamar afindViewById()
cada vez quegetView
métodogetView
. Ver List14 en API demos . - Cree un diseño genérico que cumpla con todas las combinaciones de propiedades y oculte algunos elementos si la posición actual no lo tiene.
Espero que eso te ayude. Si pudiera proporcionar algún código auxiliar XML con su estructura de datos e información sobre cómo desea asignarlo exactamente en la fila, le podría dar un consejo más preciso. Por píxel.