2012-07-06 12 views
36

Tak jak w przypadku, czy ELEMENTY ListView mogą być Fragmentami. Wiem, że możesz przypisać XML TextView do ListView, aby zmienić jego wygląd, ale możesz dodać Fragments do ListView.Czy ListView może zawierać Fragmenty

Na przykład: Mam Fragment. Kod XML wspomnianego fragmentu zawiera obraz, kilka dużych widoków tekstowych i niewielki tekst TextView. Kod klasy Fragment otrzymuje pakiet, a następnie na podstawie zawartości zapełnia odpowiednio widok TextView i ImageView. Zarówno fragment kodu XML, jak i fragment kodu działają bez problemu (Mogę wyświetlać pojedynczy fragment w porządku). Mam FragmentActivity, w którym chcę wyświetlić wyżej wymienioną listę Fragmentów. Oto kod używam próbować zapełnić ListView wewnątrz widzenia FragmentActivity za:

ArrayList<Fragment> fragList = new ArrayList<Fragment>(); 
    Fragment fragment = Fragment.instantiate(this, TileItem.class.getName()); 
    Bundle bundle = new Bundle(); 
    bundle.putInt("key", 0); 
    fragment.setArguments(bundle); 
    fragList.add(fragment); 

    ArrayAdapter<Fragment> adapter = new ArrayAdapter<Fragment>(this, R.layout.tile_item, fragList); 
    listItems.setAdapter(adapter); 

Oto mój sposób myślenia na ten temat. Tworzę ArrayList of Fragments, aby pomieścić wszystkie moje utworzone instancje. Następnie utworzę Fragment, utworzę Paczkę, dodaję dane do Paczki (aby Fragment mógł poprawnie uporządkować dane w jej Widoku), dodałem Paczkę do Fragmentu, a na koniec dodałem Fragment do ArrayList. Następnie tworzę ArrayAdapter, dodam układ elementów, którego chcę użyć, oraz listę Fragmentów, które stworzyłem; następnie ustaw ListView do odczytu z mojego adaptera.

Każdy, kto uruchomi ten kod, najprawdopodobniej uzyska NPE @ tworząc ArrayAdapter. Co daje? Czy to możliwe? Zanim zacznę się nad tym zastanawiać, czy ktoś może mi powiedzieć, że marnuję czas? Czy istnieje lepszy sposób? Myślałem o używaniu ScrollView, ale tak wiele funkcji ListView musiałoby zostać ponownie zaimplementowane, a nienawidzę - nienawidzić - nienawidzić wymyślania koła, gdy nie jest to konieczne.

Dziękuję wszystkim, którzy czytają, a szczególnie dziękuję za przemyślenia, jeśli zdecydujesz się je opuścić. Próbowałem wyszukiwać ustalonej odpowiedzi, ale wydaje mi się, że są to tylko pytania/strony internetowe dotyczące korzystania z ListView WEWNĄTRZ Fragmentu; nie używa fragmentów JAK ELEMENTY ListView

Edytuj: Podjąłem poniższe sugestie i zacząłem badać więcej. Od sposobu, w jaki się pojawiają, powinienem móc użyć niestandardowego adaptera, który nadyma fragmenty zamiast tylko wyprostować budynek z XML (z braku lepszego sposobu na opisanie procesu) Jednak moja obecna implementacja rzuca NPE podczas próby ustawienia adapter.

Oto mój kod niestandardowy adapter (skrócona dla zwięzłość):

public class AdapterItem extends ArrayAdapter<Fragment> { 

Context c; 
List<Fragment> f; 

public AdapterItem(Context c, List<Fragment> f) { 
    super(c, R.layout.tile_item, f); 
    this.c = c; 
    this.f = f; 
} 

@Override 
public View getView(int pos, View v, ViewGroup vg) { 
    LayoutInflater i = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    return i.inflate(R.layout.tile_item, vg, false); 
} 

}

i oto jak ja jej realizacji:

ArrayList<Fragment> fragList = new ArrayList<Fragment>(); 
    Fragment fragment = Fragment.instantiate(this, TileItem.class.getName()); 
    Bundle bundle = new Bundle(); 
    bundle.putInt("key", 0); 
    fragment.setArguments(bundle); 
    fragList.add(fragment); 

    AdapterItem adapter = new AdapterItem(this, fragList); 
    adapter.add(fragment); 
    listItems.setAdapter(adapter); 

Tak minęło kilka dni i jestem prawie pewien, że ten wątek został pochowany. Jednak pomyślałem, że dodam jedną ostatnią aktualizację na wypadek, gdyby ktoś chciał to wypróbować, a wyszukiwarka google je tutaj. W mojej implementacji otrzymuję NPE, gdy ListView otrzymuje adapter. Nie trzeba chirurga rakietowego, aby zorientować się, że to na pewno adapter, a nie ListView, który zgłasza błąd. Dla mojego życia nie mogę zrozumieć, dlaczego ...

W każdym razie myślę, że mam jakiś pomysł. Po pierwsze, krótka historia: chwilę temu próbowałem stworzyć FragmentTransactions w FragmentDialog. Za każdym razem, gdy próbowałem to zrobić, otrzymam NPE. Ostatecznie, poprzez wiele badań, odkryłem, że przyczyna dotyczyła sposobu, w jaki Fragmenty są instancjami. Kiedy nazywa się Fragment, potrzebuje kontekstu z jego rodzica. Ponieważ rodzicem Dialogu jest Działalność, która go rozpoczęła, sam Dialog nie spełniał koniecznych kryteriów.Sądzę, że przy próbie dodania fragmentów do ListView również tak jest. Ponieważ ListView nie spełnia warunków umowy z instancją Fragmenta, rzuca NPE, a tym samym pozostawia mnie wiszącego i powraca do konwencji. D @ mn ... Naprawdę miałem nadzieję, że będę w stanie to zrobić. Użycie Fragmentów zamiast zwykłego XMLa znacznie ułatwiłoby porządkowanie/przeszukiwanie listy. No cóż ... chyba nie da się tego zrobić, na wypadek gdyby ktoś się zastanawiał.

+2

Dlaczego nawet * chcesz * Fragmenty być "ELEMENTY z ListView"? Deweloperzy tworzą od lat 'ListView's bez fragmentów swoich elementów. Co spodziewasz się zyskać, czyniąc je fragmentami? – CommonsWare

+1

Cóż, dla jednego spodziewam się móc pokazać więcej niż zwykły tekstView wewnątrz listy (w rzeczywistości będę mógł pokazać obraz i kilka dodatkowych linii tekstu). Ponadto będę mógł wielokrotnie używać kodu fragmentu z różnych działań bez powielania kodu. Widziałem też przewijane listy przedmiotów, które są bardziej dynamiczne niż zwykły widok tekstowy (jak listy w Google Play, są bardziej złożone niż tylko widok tekstu). Jak to osiągnąć? Czy ten sam efekt nie byłby możliwy (łatwiejszy) za pomocą Fragmentu? – user1449018

+3

Możesz to zrobić, używając niestandardowych adapterów i widoków zawyżonych z pliku XML dla swoich wierszy. –

Odpowiedz

8

Powiedziałbym, że nie można tego zrobić, ponieważ umieszczenie fragmentu w Liście list oznaczałoby, że fragment można pomnożyć przez wiele kontenerów. Kiedy używasz FragmentManager do utworzenia fragmentu, jest on oznaczony identyfikatorem, co ułatwia ponowne ładowanie i zmianę orientacji oraz inne zmiany konfiguracji. Zachęca również do korzystania z wielu konfiguracji urządzeń.

Fragment jest w rzeczywistości podzbiorem działania. Czy kiedykolwiek miałbyś aktywność jako część listy? Zdecydowanie nie (powinno być odpowiedzią!) !!!

Co więcej, nie jest bardzo użyteczne ciągłe dołączanie() i odłączanie() fragmentu podczas wchodzenia i wychodzenia z widoku (komórki są poddawane recyklingowi). Są to wszystkie kosztowne operacje, których nie powinien zajmować ListView. Listy powinny szybko przewijać.

Z rozmowy na temat komentarzy widzę, że chcesz osiągnąć ładny kod z dobrym oddzieleniem kodu konfiguracji widoku i adaptera w działaniu. Zrób to za pomocą:

  1. Zastąpić klasę View i wykonać tam niestandardowy rysunek i konfigurację.
  2. Utwórz nową klasę, w której podasz kontekst i zestaw danych wymaganych do tego, aby przywrócić widok, który lista musi pokazać - tak zwykle robię.
  3. Zrób klasę Utils, aby zbudować swój film gdzie indziej (głupie).

Po prostu nie używaj fragmentów w listach. Nie celem użycia, do którego dążą. HTH.

3

Nie musisz używać Fragmentów.

Napisz niestandardowy ViewAdapter i poproś go o nadanie bardziej złożonego układu (lub kilku bardziej złożonych układów, jeśli naprawdę potrzebujesz), a następnie wypełnij pola układu, jeśli to konieczne.

[Poza: dla ludzi, którzy odpowiedzieli w komentarzach - użyj odpowiedzi zamiast komentarzy, jeśli faktycznie odpowiadasz na pytanie! Jeśli tylko uzyskasz więcej punktów reputacji w ten sposób!]

+0

PS: Jeśli próbujesz ponownie użyć części jednego układu w innym, spójrz na element

7

Okazuje się, że możesz utworzyć ListView, gdzie każda pozycja na liście to Fragment. Sztuką jest owijanie Fragmentu w FrameLayout.

UPDATE 9/16/2014

Nawet jeśli jest to możliwe stworzyć ListView które zawierają Fragments, to nie wygląda to dobry pomysł. Wydaje się, że jest to zdecydowanie przypadek narożny w świecie Androida i są smoki. Dla prostego fragmentu, jak ten w poniższym przykładzie, wszystko działa pięknie, ale jeśli masz złożony projekt, w którym dużo się dzieje, to prawdopodobnie nie jest to droga. Moje nowe podejście polega na przeniesieniu całego kodu związanego z GUI do View, który rozciąga się na FrameLayout i wstawia go do ListView - działa to DUŻO LEPSZE i jest bardziej zgodne z tym, w jaki sposób Android ma być używany.Jeśli potrzebujesz funkcji Fragment w innych częściach kodu, możesz po prostu użyć tej nowej View tam.

Powrót do pierwotnej odpowiedzi ...

Dodałem nową ManyFragments przykład do mojego AnDevCon 14 Fragments example app jeśli chcesz go wypróbować. Zasadniczo sprowadza się to do tej BaseAdapter, co w moim przykładzie wygląda to tak:

BaseAdapter adapter = new BaseAdapter() { 
     @Override public int getCount() { return 10000; } 
     @Override public Object getItem(int i) { return new Integer(i); } 
     @Override public long getItemId(int i) { return i; } 

     @Override 
     public View getView(int i, View view, ViewGroup viewGroup) { 

      if (view!=null){ 
       ManyListItemFragment fragment = (ManyListItemFragment) view.getTag(); 
       fragment.setCount(i); 
      } else { 
       FrameLayout layout = new FrameLayout(getActivity()); 
       layout.setLayoutParams(frameLayoutParams); 
       int id = generateViewId(); 
       layout.setId(id); 
       ManyListItemFragment fragment = new ManyListItemFragment(); 
       fragment.setCount(i); 
       getChildFragmentManager() 
         .beginTransaction() 
         .replace(id,fragment) 
         .commit(); 
       view = layout; 
       view.setTag(fragment); 
      } 

      return view; 
     } 
    }; 

W przypadku jesteś ciekawy oto generateViewId():

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 
public static int generateViewId() { 
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { 
     for (;;) { 
      final int result = sNextGeneratedId.get(); 
      // aapt-generated IDs have the high byte nonzero; clamp to the range under that. 
      int newValue = result + 1; 
      if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. 
      if (sNextGeneratedId.compareAndSet(result, newValue)) { 
       return result; 
      } 
     } 
    } else { 
     return View.generateViewId(); 
    } 
} 
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); 
+0

Czy możesz wskazać mi, jak mam utworzyć inny układ dla każdej listy pozycja – Shadow