Mam do czynienia z bardzo częstym problemem: ułożyłem działanie i teraz okazuje się, że powinno ono zawierać kilka elementów ScrollView
. Normalnym sposobem na to byłoby użycie istniejącego ListAdapter
, podłączenie go do a ListView
i BOOM miałbym moją listę pozycji.
ALE Nie powinieneś umieszczać zagnieżdżonego ListView
w a, ScrollView
ponieważ psuje przewijanie - nawet Android Lint narzeka na to.
Oto moje pytanie:
Jak podłączyć a ListAdapter
do LinearLayout
lub coś podobnego?
Wiem, że to rozwiązanie nie będzie skalowane dla wielu pozycji, ale moje listy są bardzo krótkie (<10 pozycji), więc ponowne wykorzystanie widoków nie jest tak naprawdę potrzebne. Jeśli chodzi o wydajność, mogę żyć z umieszczaniem wszystkich widoków bezpośrednio w LinearLayout
.
Jednym z rozwiązań, które wymyśliłem, byłoby umieszczenie mojego istniejącego układu działań w sekcji headerView pliku ListView
. Ale to wygląda na nadużycie tego mechanizmu, więc szukam czystszego rozwiązania.
Pomysły?
AKTUALIZACJA: Aby zainspirować właściwy kierunek, dodaję przykładowy układ, aby pokazać mój problem:
<?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>
UPDATE 2 Zmieniłem nagłówek, aby był łatwiejszy do zrozumienia (dostałem negatywną opinię, doh!).
źródło
Odpowiedzi:
Prawdopodobnie powinieneś po prostu ręcznie dodać swoje przedmioty do
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); }
EDYCJA : Odrzuciłem to podejście, gdy musiałem wyświetlić około 200 nietrywialnych elementów listy, jest bardzo powolne - Nexus 4 potrzebował około 2 sekund na wyświetlenie mojej „listy”, co było nie do przyjęcia. Więc zwróciłem się do podejścia Flo z nagłówkami. Działa znacznie szybciej, ponieważ widoki list są tworzone na żądanie podczas przewijania przez użytkownika, a nie w momencie tworzenia widoku.
Wznów: Ręczne dodawanie widoków do układu jest łatwiejsze do zakodowania (stąd potencjalnie mniej ruchomych części i błędów), ale ma problemy z wydajnością, więc jeśli masz 50 lub więcej widoków, radzę użyć podejścia nagłówkowego.
Przykład. Zasadniczo układ aktywności (lub fragmentu) przekształca się w coś takiego (nie jest już potrzebny 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"/>
Następnie w
onCreateView()
(użyję przykładu z fragmentem) musisz dodać widok nagłówka, a następnie ustawić adapter (zakładam, że identyfikator zasobu nagłówka toheader_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. } } });
źródło
ListAdapter
iListView
robi więcej magii za ekranami, których potem nie robię ręcznie.notifyDataSetChanged
? Działa to dobrze z innym adapterem, który podłączyłem do aListView
, ale wydaje się , że nie działa z tym, który podłączyłem doLinearLayout
.Trzymałbym się rozwiązania z widokiem nagłówka. Nie ma w tym nic złego. W tej chwili realizuję działanie przy użyciu dokładnie tego samego podejścia.
Oczywiście „część pozycji” jest bardziej dynamiczna niż statyczna (zmienna liczba pozycji w porównaniu z liczbą naprawionych pozycji itp.), W przeciwnym razie w ogóle nie myślisz o używaniu adaptera. Więc kiedy potrzebujesz adaptera, użyj ListView.
Implementacja rozwiązania, które wypełnia LinearLayout z adaptera, to w końcu nic innego jak zbudowanie ListView z niestandardowym układem.
Tylko moje 2 centy.
źródło
ListView
. Nie jestem pewien, czy podoba mi się ta łatka.Ustaw widok na main.xml onCreate, a następnie napompuj z 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>
źródło
Używam następującego kodu, który replikuje funkcjonalność adaptera z
ViewGroup
iTabLayout
. Dobrą rzeczą jest to, że jeśli zmienisz listę i ponownie utworzysz powiązanie, wpłynie to tylko na zmienione elementy:Stosowanie:
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)})
Dla
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])) } } }
Dla
TabLayout
mam to: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])) } } }
źródło