Un ListView peut-il contenir des Fragments?

comme dans, les éléments D'une ListView peuvent être des Fragments. Je sais que vous pouvez affecter un XML de TextView à une ListView pour changer son apparence, mais vous pouvez ajouter des Fragments dans une ListView.

Par exemple: j'ai un Fragment. Le XML pour ce Fragment contient une vue D'Image, quelques vues de texte de grand style, et une vue de texte de petit style. Le code de classe de Fragment reçoit un paquet, puis basé sur le contenu popule les TextViews et ImageView en conséquence. À la fois le Fragment XML et le code Fragment fonctionne sans problème (Je peux afficher un individu Fragment très bien). J'ai une Fragmentactivité dans laquelle je veux afficher la liste susmentionnée de Fragments. Voici le code que j'utilise pour essayer de peupler la ListView à l'intérieur de la vue de 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);

Voici mon mode de pensée sur ce point. Je fais un ArrayList de Fragments pour tenir toutes mes vues instanciées. Je crée alors un Fragment, crée un faisceau, ajoute des données au faisceau (de sorte que le Fragment can marshal data into it's Views correctly), ajouter le faisceau au Fragment, puis finalement ajouter le Fragment à L'ArrayList. Après cela, je fais un ArrayAdapter, j'ajoute la mise en page de l'élément que je veux utiliser, et la liste des Fragments que j'ai faits; puis j'installe la ListView pour lire à partir de mon adaptateur.

quiconque exécute ce code obtiendra probablement le NPE @ instantiating le ArrayAdapter. Ce qui donne? Est-ce même possible? Avant que je continue à creuser mon cerveau sur ce peut quelqu'un me dire si je suis juste de perdre mon temps? Est-il un meilleur moyen? J'ai pensé à utiliser un ScrollView, mais une grande partie des fonctionnalités D'un ListView devrait être ré-implémentée et je déteste réinventer la roue quand ce n'est pas nécessaire.

merci à tous ceux qui lisent, et surtout merci pour vos réflexions si vous décidez de les quitter. J'ai essayé de chercher une réponse établie à ceci mais tout ce que je semble trouver sont des questions / pages Web concernant l'utilisation D'un ListView à l'intérieur d'un Fragment; ne pas utiliser des Fragments COMME LES ÉLÉMENTS d'une ListView

Edit: j'ai pris les suggestions ci-dessous et j'ai commencé à enquêter davantage. De la façon dont les choses apparaissent, je devrais être en mesure d'utiliser un adaptateur personnalisé qui gonfle des fragments au lieu de simplement aplatir le bâtiment à partir de XML (faute d'une meilleure façon de décrire le processus) cependant, mon actuelle implémentation lance un NPE en essayant de définir l'adaptateur.

Voici mon code d'adaptateur personnalisé (raccourci pour souci de concision):

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

}

et voici comment je m'y suis mise en œuvre:

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

donc ça fait quelques jours et je suis presque sûr que ce fil a été enterré. Cependant, j'ai pensé que je voudrais ajouter une dernière mise à jour juste au cas où quelqu'un veut essayer ceci et une recherche google les amène ici. Ainsi, dans mon implémentation, je reçois un NPE lorsque ListView reçoit l'adaptateur. Il n'est pas nécessaire d'un chirurgien pour comprendre que c'est certainement l'adaptateur et pas le ListView lance l'erreur. Pour la vie de moi je ne peux pas comprendre pourquoi...

En tout cas, je pense que j'ai une certaine idée. Tout d'abord, une petite histoire en arrière: Il ya un certain temps, j'ai essayé de faire FragmentTransactions à l'intérieur D'un FragmentDialog. Chaque fois que je tentais de le faire, j'obtenais un NPE. Finalement, à travers de nombreuses recherches, j'ai découvert que la raison se rapportait à la façon dont les Fragments sont instanciés. Quand un Fragment est appelé, il a besoin du contexte de son parent. Comme le parent D'une boîte de dialogue est l'activité qui l'a lancée, la boîte de dialogue elle-même ne répondait pas aux critères nécessaires. Je crois que lorsqu'on tente d'ajouter des fragments à un ListView, c'est aussi le cas. Comme ListView ne respecte pas l'accord en instanciant un Fragment, il lance le NPE et me laisse donc en plan et retourne aux conventions. D@mn...J'avais vraiment espéré que je serais capable de faire cela. Utiliser des Fragments plutôt que du XML simple aurait rendu tellement plus facile d'organiser/rechercher par le biais de la liste. Oh bien... je suppose que c'est impossible au cas où quelqu'un se poserait la question.

36
demandé sur user1449018 2012-07-06 17:38:53

3 réponses

je dirais que ce n'est pas possible car mettre un fragment dans un ListView signifierait que le fragment peut être multiplié à travers plusieurs conteneurs. Lorsque vous utilisez FragmentManager pour créer un fragment, il est étiqueté avec un identifiant, ce qui le rend simple à recharger et réarranger sur l'orientation et d'autres changements de configuration. Il encourage également les utilisations à travers de multiples configurations de périphériques.

Un Fragment est en fait un sous-ensemble d'une activité. Auriez-vous déjà une activité dans une liste? Certainement pas (devrait être la réponse!)!!!

de plus, il n'est pas très utile d'attacher() et détacher() un fragment de façon continue pendant qu'ils se déplacent dans et hors de la vue (les cellules sont recyclées). Ce sont des opérations coûteuses qu'un ListView ne devrait pas traiter. Les listes devraient défiler rapidement.

de la conversation sur les commentaires, je peux voir que vous voulez obtenir du code agréable avec une bonne séparation du code d'installation de vue et adaptateur dans l'activité. Faire avec soit:

  1. Remplacer View classe et de faire de votre dessin personnalisé et d'installation.
  2. créer une nouvelle classe, dans laquelle vous fournissez un contexte et un ensemble de données nécessaires pour qu'il vous ramène la vue qu'une liste doit montrer - c'est ce que je fais habituellement.
  3. Utils classe pour construire votre vidéo ailleurs (stupide).

N'utilisez pas de Fragments dans les listes. Pas le cas d'utilisation qu'ils visent. HTH.

8
répondu dineth 2013-01-16 23:53:23

Il s'avère que vous pouvez créer un ListView où chaque élément de listView est un Fragment. Le truc est d'envelopper le Fragment dans un FrameLayout.

UPDATE 9/16/2014

Même si c'est possible pour créer un ListView qui contiennent Fragments, il ne semble pas que c'est une bonne idée. Cela semble certainement être un cas de coin dans le monde Android et il ya des dragons. Pour un fragment simple comme celui de l'exemple ci-dessous tout fonctionne à merveille, mais si vous avez un projet complexe, avec beaucoup de choses en elle alors ce n'est probablement pas la voie à suivre. Ma nouvelle approche est de tirer l'ensemble de l'interface graphique liées code dans une balise View qui s'étend FrameLayout, et insérez-la dans un la ListView -- cela fonctionne beaucoup mieux et est plus conforme à la façon dont Android s'attend à être utilisé. Si vous avez besoin de la fonctionnalité d'un Fragment dans d'autres parties de votre code, vous pouvez simplement utiliser ce nouveau View là aussi.

Retour à la réponse originale...

j'ai ajouté un nouveau ManyFragments exemple de mon AnDevCon 14 Fragments exemple app si vous voulez l'essayer. Essentiellement, il descend le BaseAdapter, qui dans mon exemple ressemble à ceci:

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

Dans le cas où vous êtes curieux, voici 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);
7
répondu JohnnyLambada 2014-09-16 15:35:09

Vous n'avez pas besoin d'utiliser des Fragments.

Écrire un ViewAdapter personnalisé et le faire gonfler un layout plus complexe (ou peut-être plusieurs layouts plus complexes si vous avez besoin d'obtenir vraiment fantaisie) puis remplir les champs du layout comme nécessaire.

[mise à part: aux personnes qui ont répondu dans les commentaires -- veuillez utiliser les réponses plutôt que les commentaires si vous répondez réellement à la question! Si seulement parce que vous obtenez plus de points de réputation!]

3
répondu Dale Wilson 2013-03-19 20:46:00