tipos studio programacion personalizado llenar example desplegable adaptadores android android-layout android-arrayadapter

android - studio - Usar un ListAdapter para rellenar un LinearLayout dentro de un diseño ScrollView



manual de programacion android pdf (4)

Me enfrenta a un problema muy común: di una actividad y ahora resulta que debería mostrar algunos elementos dentro de este ScrollView . La forma normal de hacer eso sería usar el ListAdapter existente, conectarlo a un ListView y BOOM Tendría mi lista de elementos.

PERO No debería colocar ListView anidado en ScrollView ya que estropea el desplazamiento, incluso Android Lint se queja de ello.

Así que aquí está mi pregunta:

¿Cómo conecto un ListAdapter a LinearLayout o algo similar?

Sé que esta solución no se escalará para muchos artículos, pero mis listas son muy cortas (<10 elementos) por lo que no es realmente necesario reutilizar las vistas. En cuanto al rendimiento, puedo vivir colocando todas las vistas directamente en LinearLayout .

Una solución que se me ocurrió fue colocar mi diseño de actividad existente en la sección headerView de ListView . Pero esto parece abusar de este mecanismo, así que estoy buscando una solución más limpia.

Ideas?

ACTUALIZACIÓN: para inspirar la dirección correcta, agrego un diseño de muestra para mostrar mi problema:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/news_detail_layout" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:visibility="visible"> <ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFF" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:paddingLeft="@dimen/news_detail_layout_side_padding" android:paddingRight="@dimen/news_detail_layout_side_padding" android:paddingTop="@dimen/news_detail_layout_vertical_padding" android:paddingBottom="@dimen/news_detail_layout_vertical_padding" > <TextView android:id="@+id/news_detail_date" android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center_horizontal" android:text="LALALA" android:textSize="@dimen/news_detail_date_height" android:textColor="@color/font_black" /> <Gallery android:id="@+id/news_detail_image" android:layout_height="wrap_content" android:layout_width="fill_parent" android:paddingTop="5dip" android:paddingBottom="5dip" /> <TextView android:id="@+id/news_detail_headline" android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center_horizontal" android:text="Some awesome headline" android:textSize="@dimen/news_detail_headline_height" android:textColor="@color/font_black" android:paddingTop="@dimen/news_detail_headline_paddingTop" android:paddingBottom="@dimen/news_detail_headline_paddingBottom" /> <TextView android:id="@+id/news_detail_content" android:layout_height="wrap_content" android:layout_width="fill_parent" android:text="Here comes a lot of text so the scrollview is really needed." android:textSize="@dimen/news_detail_content_height" android:textColor="@color/font_black" /> <!--- HERE I NEED THE LIST OF ITEMS PROVIDED BY THE EXISTING ADAPTER. They should be positioned at the end of the content, so making the scrollview smaller is not an option. ----> </LinearLayout> </ScrollView> </LinearLayout>

ACTUALIZACIÓN 2 Cambié el título para que sea más fácil de entender (¡recibí un voto negativo, doh!).


Establezca su vista en main.xml onCreate, luego infle desde row.xml

main.xml

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="450dp" > <ListView android:id="@+id/mainListView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@+id/size" android:layout_below="@+id/editText1" android:gravity="fill_vertical|fill_horizontal" android:horizontalSpacing="15dp" android:isScrollContainer="true" android:numColumns="1" android:padding="5dp" android:scrollbars="vertical" android:smoothScrollbar="true" android:stretchMode="columnWidth" > </ListView> <TextView android:id="@+id/size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:background="#ff444444" android:gravity="center" android:text="TextView" android:textColor="#D3D3D3" android:textStyle="italic" /> </EditText> </RelativeLayout>

row.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:paddingTop="3dp"> <TextView android:id="@+id/rowTextView" android:layout_width="0dip" android:layout_height="41dp" android:layout_margin="4dp" android:layout_weight="2.83" android:ellipsize="end" android:gravity="center_vertical" android:lines="1" android:text="John Doe" android:textColor="@color/color_white" android:textSize="23dp" > </TextView> </LinearLayout>


Me quedaría con la solución de vista de encabezado. No hay nada de malo en eso En este momento estoy implementando una actividad usando el mismo enfoque.

Obviamente, la "parte del elemento" es más dinámica que estática (varía el recuento de elementos frente a la cantidad de elementos fijos, etc.), de lo contrario, no se te ocurrirá utilizar un adaptador. Entonces, cuando necesite un adaptador, use ListView.

Implementar una solución que rellena un LinearLayout desde un adaptador no es más que construir un ListView con un diseño personalizado.

Solo mis 2 centavos.


Probablemente deberías agregar manualmente tus artículos a LinearLayout :

LinearLayout layout = ... // Your linear layout. ListAdapter adapter = ... // Your adapter. final int adapterCount = adapter.getCount(); for (int i = 0; i < adapterCount; i++) { View item = adapter.getView(i, null, null); layout.addView(item); }

EDITAR : Rechacé este enfoque cuando necesitaba mostrar alrededor de 200 elementos de la lista no triviales, es muy lento - Nexus 4 necesitaba alrededor de 2 segundos para mostrar mi "lista", eso era inaceptable. Así que recurrí al enfoque de Flo con encabezados. Funciona mucho más rápido porque las vistas de lista se crean a petición cuando el usuario se desplaza, no en el momento en que se crea la vista.

Curriculum vitae: La adición manual de vistas al diseño es más fácil de codificar (por lo tanto, menos partes móviles y errores), pero adolece de problemas de rendimiento, por lo que si tiene 50 vistas o más, le aconsejo utilizar el enfoque de encabezado.

Ejemplo. Básicamente, el diseño de la actividad (o fragmento) se transforma en algo como esto (ya no se necesita ScrollView):

<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/my_top_layout" android:layout_width="fill_parent" android:layout_height="fill_parent"/>

Luego, en onCreateView() (voy a usar un ejemplo con un fragmento), necesitas agregar una vista de encabezado y luego establecer un adaptador (supongo que el ID del recurso del encabezado es header_layout ):

ListView listView = (ListView) inflater.inflate(R.layout.my_top_layout, container, false); View header = inflater.inflate(R.layout.header_layout, null); // Initialize your header here. listView.addHeaderView(header, null, false); BaseAdapter adapter = // ... Initialize your adapter. listView.setAdapter(adapter); // Just as a bonus - if you want to do something with your list items: view.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // You can just use listView instead of parent casted to ListView. if (position >= ((ListView) parent).getHeaderViewsCount()) { // Note the usage of getItemAtPosition() instead of adapter''s getItem() because // the latter does not take into account the header (which has position 0). Object obj = parent.getItemAtPosition(position); // Do something with your object. } } });


Utilizo el siguiente código que replica la funcionalidad del adaptador con ViewGroup y TabLayout . Lo bueno de esto es que si cambias tu lista y vuelves a vincularla, solo afectará los elementos cambiados:

Uso:

val list = mutableListOf<Person>() layout.bindChildren(list, { it.personId }, { bindView(it) }, {d, t ->bindView(d, t)}) list.removeAt(0) list+=newPerson layout.bindChildren(list, { it.personId }, { bindView(it) }, {d, t ->bindView(d, t)})

Para ViewGroups :

fun <Item, Key> ViewGroup.bindChildren(items: List<Item>, id: (Item) -> Key, view: (Item) -> View, bind: (Item, View) -> Unit) { val old = children.map { it.tag as Key }.toList().filter { it != null } val new = items.map(id) val add = new - old val remove = old - new val keep = new.intersect(old) val tagToChildren = children.associateBy { it.tag as Key } val idToItem = items.associateBy(id) remove.forEach { tagToChildren[it].let { removeView(it) } } keep.forEach { bind(idToItem[it]!!, tagToChildren[it]!!) } add.forEach { id -> view(idToItem[id]!!).also { it.tag = id }.also { addView(it, items.indexOf(idToItem[id])) } } }

Para TabLayout tengo esto:

fun <Item, Key> TabLayout.bindTabs(items: List<Item>, toKey: (Item) -> Key, tab: (Item) -> TabLayout.Tab, bind: (Item, TabLayout.Tab) -> Unit) { val old = (0 until tabCount).map { getTabAt(it)?.tag as Key } val new = items.map(toKey) val add = new - old val remove = old - new val keep = new.intersect(old) val tagToChildren = (0 until tabCount).map { getTabAt(it) }.associateBy { it?.tag as Key } val idToItem = items.associateBy(toKey) remove.forEach { tagToChildren[it].let { removeTab(it) } } keep.forEach { bind(idToItem[it]!!, tagToChildren[it]!!) } add.forEach { key -> tab(idToItem[key]!!).also { it.tag = key }.also { addTab(it, items.indexOf(idToItem[key])) } } }