2012-07-06 16 views
36

Come in, gli ELEMENTI di un ListView possono essere frammenti. So che puoi assegnare un XML TextView a un ListView per cambiare il suo aspetto, ma puoi aggiungere Frammenti in un ListView.Un ListView può contenere frammenti

Ad esempio: ho un frammento. L'XML di detto frammento contiene un ImageView, un paio di TextViews di grandi dimensioni e un TextView di piccolo stile. Il codice classe Fragment riceve un pacchetto, quindi in base ai contenuti popola di conseguenza TextView e ImageView. Sia il Fragment XML che il Fragment codice funzionano senza problemi (Posso visualizzare un Frammento singolo bene). Ho un FragmentActivity in cui voglio visualizzare l'elenco di Fragments di cui sopra. Ecco il codice che sto usando per cercare di popolare il ListView all'interno di vista del FragmentActivity:

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); 

Ecco il mio modo di pensare su questo. Faccio un ArrayList of Fragments per contenere tutte le mie viste istanziate. Creo quindi un frammento, creo un pacchetto, aggiungo dati al pacchetto (in modo che il frammento possa effettuare il marshalling dei dati nelle sue viste correttamente), aggiungi il pacchetto al frammento, quindi aggiungi il frammento alla lista degli array. Dopo di ciò, creo un ArrayAdapter, aggiungo il layout degli elementi che voglio usare e l'elenco dei Frammenti che ho creato; quindi impostare ListView per leggere dal mio adattatore.

Chiunque esegua questo codice otterrà probabilmente NPE @ che istanzia l'ArrayAdapter. Cosa dà? È possibile? Prima che continui a tormentarmi, qualcuno può dirmi se sto solo perdendo il mio tempo? C'è un modo migliore? Stavo pensando di usare una ScrollView, ma tanta parte della funzionalità di un ListView dovrebbe essere nuovamente implementata e odio-odio-odiare reinventando la ruota quando non è necessario.

Grazie a chiunque stia leggendo, e soprattutto grazie per i vostri pensieri se decidete di lasciarli. Ho provato a cercare in giro una risposta affermata a questo, ma tutto quello che mi sembra di trovare sono domande/pagine web riguardanti l'uso di una ListView INSIDE di un frammento; non usare Frammenti COME GLI ELEMENTI di un ListView

Modifica: ho preso i suggerimenti di seguito e ho iniziato a indagare di più. Dal modo in cui appaiono le cose dovrei essere in grado di usare un adattatore personalizzato che gonfia i frammenti invece di costruire semplicemente da XML (per mancanza di un modo migliore per descrivere il processo) Tuttavia, la mia attuale implementazione sta lanciando un NPE quando provo a impostare l'adattatore.

Ecco il mio codice adattatore personalizzato (abbreviato per brevità):

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); 
} 

}

e qui è come sto attuazione:

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); 

quindi è stato un paio di giorni e sono abbastanza sicuro che questo thread è stato sepolto. Tuttavia, ho pensato di aggiungere un ultimo aggiornamento nel caso qualcuno volesse provare questo e una ricerca su Google li porta qui. Quindi nella mia implementazione sto ricevendo un NPE quando ListView riceve l'adattatore. Non ci vuole un chirurgo di razzo per capire che è certamente l'adattatore e non il ListView a lanciare l'errore. Per la vita di me non riesco a capire perché però ...

In ogni caso, penso di avere qualche idea però. Prima, una piccola storia passata: un po 'di tempo fa stavo cercando di fare FrammentTransactions all'interno di un FragmentDialog. Ogni volta che ho provato a farlo, avrei ottenuto un NPE. Alla fine, attraverso molte ricerche, ho scoperto che la ragione riguardava il modo in cui i Frammenti sono istanziati. Quando viene chiamato un frammento ha bisogno del contesto dal suo genitore. Poiché il genitore di una finestra di dialogo è l'attività che lo ha avviato, la finestra di dialogo stessa non ha soddisfatto i criteri necessari.Credo che quando si tenta di aggiungere frammenti a un ListView, questo è anche il caso. Dal momento che il ListView non soddisfa l'accordo con l'istanziamento di un frammento getta l'NPE e, quindi, mi lascia sospeso e tornare alle convenzioni. D @ mn ... avevo davvero sperato che sarei stato in grado di farlo. Usare Frammenti invece di semplici XML avrebbe reso molto più facile organizzare/cercare nella lista. Oh beh ... suppongo che non possa essere fatto nel caso qualcuno si stia chiedendo.

+2

Perché anche * vuole * frammenti di essere "ELEMENTI di un ListView"? Gli sviluppatori hanno creato 'ListView's per anni senza che i loro elementi fossero frammenti. Cosa ti aspetti di guadagnare dal farli diventare frammenti? – CommonsWare

+1

Bene, per esempio mi aspetto di poter mostrare più di una semplice TextView all'interno di una lista (infatti sarò in grado di mostrare un'immagine e alcune righe di testo in più). Inoltre, sarò in grado di riutilizzare il codice frammento più e più volte da diverse attività senza duplicare il codice. Ho anche visto elenchi a scorrimento di elementi più dinamici di una semplice TextView (come gli elenchi di Google Play, sono più complessi di un semplice TextView). Come si ottiene? Lo stesso effetto non sarebbe possibile (più facile) usando un frammento? – user1449018

+3

Puoi farlo utilizzando l'adattatore e le viste personalizzate gonfiati da XML per le tue righe. –

risposta

8

Direi che non è possibile fare come mettere un frammento in un ListView significherebbe che il frammento può essere moltiplicato su più contenitori. Quando si utilizza FragmentManager per creare un frammento, viene contrassegnato con un identificatore, rendendo semplice ricaricare e riorganizzare l'orientamento e altre modifiche di configurazione. Incoraggia anche gli usi attraverso più configurazioni del dispositivo.

Un frammento è in realtà un sottoinsieme di un'attività. Avresti mai un'attività come parte di una lista? Sicuramente no (dovrebbe essere la risposta!) !!!

Inoltre, non è molto utile collegare() e staccare() un frammento continuamente mentre si spostano dentro e fuori dalla vista (le celle vengono riciclate). Queste sono tutte operazioni costose che un ListView non dovrebbe gestire. Le liste dovrebbero scorrere velocemente.

Dalla conversazione sui commenti, posso vedere che si desidera ottenere un codice piacevole con una buona separazione del codice di configurazione della vista e dell'adattatore nell'attività. Fatelo con:

  1. Ignora la classe View e esegui il disegno e l'installazione personalizzati lì.
  2. Creare una nuova classe, in cui fornisci un contesto e un set di dati richiesti per riportarti indietro la vista che deve essere visualizzata da un elenco: questo è ciò che faccio di solito.
  3. Avere una classe Utils per creare il tuo video altrove (sciocco).

Basta non usare Frammenti nelle liste. Non il caso d'uso a cui mirano. HTH.

3

Non è necessario utilizzare Frammenti.

Scrivere un ViewAdapter personalizzato e farlo gonfiare un layout più complesso (o forse diversi layout più complessi se è necessario essere veramente fantasiosi) quindi compilare i campi del layout secondo necessità.

[A parte: alle persone che hanno risposto nei commenti, utilizzare le risposte anziché i commenti se si sta effettivamente rispondendo alla domanda! Se solo perché ottieni più punti reputazione in questo modo!]

+0

PS: Se stai cercando di riutilizzare parti di un layout in un altro, dai un'occhiata all'elemento

7

Si scopre che è possibile creare un ListView in cui ogni elemento in listView è un Fragment. Il trucco è avvolgere il frammento in un FrameLayout.

UPDATE 9/16/2014

Anche se è possibile creare un ListView che contengono Fragments, non sembra come se fosse una buona idea. Questo sembra essere sicuramente un caso d'angolo nel mondo di Android e ci sono i draghi. Per un semplice frammento come quello nell'esempio seguente, tutto funziona perfettamente, ma se si dispone di un progetto complesso con molte attività in corso, probabilmente non è questa la strada da percorrere. Il mio nuovo approccio è quello di estrapolare tutto il codice relativo alla GUI in un e inserirlo nello ListView - questo funziona MOLTO MEGLIO ed è più in linea con il modo in cui Android si aspetta di essere utilizzato.Se hai bisogno della funzionalità di un Fragment in altre parti del tuo codice, puoi semplicemente usare anche questo nuovo View.

Torna alla risposta originale ...

Ho aggiunto un nuovo ManyFragments esempio al mio AnDevCon 14 Fragments example app se si vuole provare. In sostanza si tratta della la BaseAdapter, che nel mio esempio si presenta così:

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; 
     } 
    }; 

Nel caso foste curiosi ecco 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

Puoi guidarmi come suppongo di creare layout diversi per ogni elenco articolo – Shadow

Problemi correlati