2011-12-16 14 views
15

Mam widok LinearLayout, który zawiera już kilka elementów. Chcę programowo dodać do niego o wiele więcej widoków. A ponieważ jest to wewnątrz ScrollView, wszystko zostanie przewinięte.Jaki jest najszybszy sposób dodania kilku widoków do LinearLayout?

Więc to, co robię, to przeglądanie mojej listy i dodawanie nowych instancji do mojego niestandardowego widoku. Ten widok niestandardowy nadpisuje układ XML i dodaje kilka metod.

To podejście działa dobrze. Problem polega na tym, że jest bardzo powolny, nawet bez szalonego kodu ... Lista z 10 elementami zajmuje około 500 ms, aby utworzyć instancję. Z punktu widzenia wygody użytkownika trudno to połknąć.

Moje pytanie brzmi: czy to jest właściwe/najlepsze podejście? Wydaje się, że system Android zajmuje dużo czasu nadymaniem układu, mimo że "R.layout.my_list_item" jest bardzo prosty. Zastanawiam się, czy istnieje sposób, aby ponownie użyć "nadęty" układ dla dodatkowych widoków, czy raczej buforowanie bardziej złożonych analiz?

Próbowałem to zrobić z ListView (i adapter i opakowanie) i wydaje się, że jest znacznie szybciej. Problem polega na tym, że nie mogę użyć zwykłego ListView; mój układ jest bardziej złożony niż prosta lista (sama wersja LinearLayout zawiera dodatkowe niestandardowe ikony i ma innego rodzica z jeszcze większą liczbą wyświetleń, zanim zostanie zapakowana przez ScrollView).

Ale czy istnieje sposób użycia adaptera do LinearLayout? Czy byłoby to szybsze niż próba samodzielnego dodania widoków?

Każda pomoc jest doceniana. Chciałbym zrobić to szybciej.

Kod następuje.

Główna działalność:

// The LinearLayout that will contain everything 
lineList = (LinearLayout) findViewById(R.id.lineList); 

// Add a lot of items for testing 
for (int i = 0; i < 10; i++) { 
    addListItem("Item number " + i); 
} 

protected void addListItem(String __title) { 
    MyListItem li; 
    li = new MyListItem(this); 
    li.setTitle(__title); 
    lineList.addView(li);  
} 

MyListItem:

public class MyListItem extends RelativeLayout { 

    protected TextView textTitle; 

    public MyListItem(Context __context) { 
     super(__context); 
     init(); 
    } 

    public MyListItem(Context __context, AttributeSet __attrs) { 
     super(__context, __attrs); 
     init(); 
    } 

    public MyListItem(Context __context, AttributeSet __attrs, int __attrsdefStyle) { 
     super(__context, __attrs, __attrsdefStyle); 
     init(); 
    } 

    protected void init() { 
     // Inflate the XML layout 
     LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     inflater.inflate(R.layout.my_list_item, this); 

     // Create references 
     textTitle = (TextView)findViewById(R.id.textTitle); 
    } 

    public void setTitle(String __text) { 
     textTitle.setText(__text); 
    } 
} 

Co próbuję wykonać jest to. Rozważmy następujący układ:

Basic layout

Ten układ jest FrameLayout (karton) zawierający ImageView (szary) w TextView (wewnętrzny prostokąta, na górze) i LinearLayout (wewnętrzny prostokąta, na dole). Ten prostokąt LinearLayout jest tym, który dynamicznie zapełniam kilkoma elementami.

Po wypełnić go, chcę końcowy wynik będzie to (gdzie każdy nowy prostokąt jest nowy MyListItem instancja):

Populated layout

Oznacza to, że wszystko jest przewijana (obraz tła, na przykład jest wyrównany na górze). LinearLayout nie można przewijać samodzielnie (wszystko inne następuje), dlatego w moim przypadku ListView, z tego, co wiem, nie działałoby zbyt dobrze .

+1

Ile elementów posiadasz w tej czynności i czy posiadasz niestandardową klasę dziedziczącą elementy interfejsu użytkownika? –

+0

ListViews są tak szybkie, ponieważ przetwarzają układy przedmiotów. Nadymają liczbę układów przedmiotów wymaganych do wypełnienia ekranu. Gdy przewiniesz w dół, jeden widok na górze zostanie ukryty, a jeden na dole zostanie wyświetlony. Listview pobiera stary, niewidoczny z góry i umieszcza go z nowymi danymi na dole. Unika kosztownej inflacji. Ogólnie: tutaj wygląda * bardzo * nieefektywnie, jestem prawie pewien, że to, co chcesz zrobić, można osiągnąć dzięki listview. Jeśli pokażesz, co wypróbowałeś i opublikujesz szkic zamierzonego układu, pomożemy Ci go wymyślić. –

+0

Paul: mało elementów. Może 4 lub 5. Tak, niektóre z nich są niestandardowymi klasami, które dziedziczą po podstawowej strukturze. alextsc: dzięki za wyjaśnienie. Dodałem schemat, który lepiej wyjaśnia moją sytuację, więc jest jasne, czy i jak mogę zamiast tego użyć ListView. – zeh

Odpowiedz

7

3 opcje:

  1. zastąpić wszystko z ListView, z drugim rodzicem i niestandardowych ikon w widoku nagłówka dla ListView. ListView jest szybszy, ponieważ tworzy widoki tylko wtedy, gdy ich potrzebuje.

  2. Programowo utworzyć zawartość my_list_item zamiast pompowania, może być szybciej

  3. Zastosowanie ViewStubs może pozwolić, aby załadować widoki na żądanie.

  4. Może to nie ładuje widoków, ale dane? w takim przypadku przygotuj dane w wątku tła.

+0

Dzięki za tę prostą, praktyczną odpowiedź. Dodałbym "utwórz widoki w oddzielnym wątku" do listy (jak sugerował użytkownik1031312 w komentarzu poniżej). Poza tym uważam, że dobrze podsumowujesz możliwe rozwiązania. – zeh

1

Po prostu użyj ListView!

Jest to najłatwiejszy w konfiguracji i najłatwiejszy w utrzymaniu. Użytkownik definiuje układ XML dla wiersza listy i układ XML dla widoku, który przechowuje całą listę. ListAdapter zajmie się resztą.

Wystarczy utworzyć:

List<HashMap<String, String>> services = new ArrayList<HashMap<String, String>>(); 

... i pętli danych, aby dodać jak najwięcej przedmiotów, jak chcesz do mapy. Następnie ustaw tę mapę na swój ListAdapter. Niezależnie od tego, czy 10 elementów, czy 100 elementów, ListAdapter utworzy listę z tak wieloma pozycjami.

przykład:

public void updateProductList(String searchTerm) { 
     createOrOpenDB(this); 
     Cursor cursor = (searchTerm!=null)? dbAdapter.fetchWhere(TBL_NAME, KEY_NAME+" LIKE '%"+searchTerm+"%'") 
       : dbAdapter.fetchAll(TBL_NAME); 
     int rows = cursor.getCount(); 
     if (rows<=0) return; 
     services.clear(); //clear current list 
     for (int i=0; i<rows; i++) { 
      HashMap<String, String> map = new HashMap<String, String>(); 
      cursor.moveToPosition(i); 
      map.put(KEY_NAME, "" + cursor.getString(cursor.getColumnIndex(KEY_NAME))); 
      map.put(KEY_DESC, "" + cursor.getString(cursor.getColumnIndex(KEY_DESC))); 
      map.put(KEY_COST, "" + cursor.getDouble(cursor.getColumnIndex(KEY_COST))); 
      services.add(map); 
     } 
     cursor.close(); 
     closeDB(); 

     ListAdapter adapter = new SimpleAdapter(this, services, R.layout.products_view_row, 
       new String[] {KEY_NAME, KEY_DESC, KEY_COST}, 
       new int[] {R.id.listViewText1, R.id.listViewText2, R.id.listViewText3}); 
     setListAdapter(adapter); 
    } 
+0

Dzięki. Stworzyłem 'ListView' z odpowiednim adapterem w innych częściach tej samej aplikacji, więc problem nie dotyczy implementacji' ListView'. Powodem, dla którego nie używam ListView tutaj jest to, że nie wydaje się, aby osiągnąć to, czego potrzebuję (przewijanie równolegle innych nie-listowych, nie-nagłówkowych elementów). Bardzo chciałbym, aby mi się udowodniono, ale odpowiedź jest bardziej skomplikowana niż "po prostu użyj listview". – zeh

+0

Ohh Widzę. To, czego chcesz, to: http://stackoverflow.com/questions/3192595/android-listview-that-does-not-scroll (patrz zaakceptowana odpowiedź) w połączeniu z ScrollView, aby zamknąć cały układ. – Ozzy

+0

Err, tak jak powiedziałem w oryginalnym poście, to jest dokładnie to, co robię, ale mam problem z wydajnością (ręczne dodawanie elementów do LinearLayout jest powolne). – zeh

2

ListView jest droga.

Mówisz, że twój układ jest zbyt skomplikowany. Ale całkiem dobrze jest nadmuchać złożony układ jako dziecko. Na przykład układ, który ma tekst, a następnie ikonę:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" > 
    <TextView 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" /> 
    <ImageView 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" /> 
</LinearLayout> 

Może być napełniany w karty jak tak:

@Override 
public View getView(final int position, View convertView, ViewGroup parent) { 
    LinearLayout root = null; 
    ImageView editImageView; 

    if (convertView == null) { 
     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     root = (LinearLayout)inflater.inflate(R.layout.item, null); 
    } else { 
     root = (LinearLayout)convertView; 
    } 
} 

Możesz być również nieco bardziej mądry w celu wsparcia jej nagłówek. Po prostu dodaj czek, jeśli indeks jest korzeniem i nadmuchaj inny widok. Ponieważ nagłówek jest jedynym, który jest inny, nadal będziesz mógł wykorzystać wszystkie pozostałe wiersze do ponownego użycia. Możesz nawet przedmuchać i przechować nagłówek i użyć go ponownie, aby całkowicie pozbyć się inflacji.

+0

Dzięki. Ale zauważ, że powiedziałem, że układ jest "bardziej złożony niż prosta lista" niż "zbyt skomplikowany" (sam układ jest prosty). Nie miałbym problemu z nadmuchaniem tego wszystkiego. Problem polega na tym, że nie widzę sposobu, w jaki mogłem sprawić, by mój układ działał z nagłówkami na liście 'ListView', biorąc pod uwagę, że mam elementy idące za listą (obraz na moim diagramie). Czy mogę umieścić obraz na górze listy 'ListView', ale pod elementami podrzędnymi? – zeh

Powiązane problemy