web-dev-qa-db-fra.com

Un ListView peut-il contenir des fragments

Comme dans, les ELEMENTS d'un ListView peuvent être des fragments. Je sais que vous pouvez affecter un XML TextView à un ListView pour changer son apparence, mais pouvez-vous ajouter des fragments dans un ListView.

Par exemple: j'ai un fragment. Le XML pour ledit fragment contient une ImageView, quelques TextViews de grand style et une TextView de petit style. Le code de classe Fragment reçoit un bundle, puis en fonction du contenu remplit les TextViews et ImageView en conséquence. Le XML du fragment et le code du fragment fonctionnent sans problème (je peux très bien afficher un fragment individuel). J'ai une FragmentActivity dans laquelle je souhaite afficher la liste des Fragments susmentionnée. Voici le code que j'utilise pour essayer de remplir le 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 ma façon de penser à ce sujet. Je fais une liste de fragments pour contenir toutes mes vues instanciées. Je crée ensuite un fragment, crée un bundle, ajoute des données au bundle (afin que le fragment puisse correctement rassembler les données dans ses vues), ajoute le bundle au fragment, puis enfin ajoute le fragment à la liste de tableaux. Après cela, je crée un ArrayAdapter, j'ajoute la disposition des éléments que je veux utiliser et la liste des fragments que j'ai créés; puis définissez ListView pour lire à partir de mon adaptateur.

Toute personne exécutant ce code obtiendra probablement le NPE @ instanciant ArrayAdapter. Ce qui donne? Est-ce seulement possible? Avant de continuer à me creuser la tête, quelqu'un peut-il me dire si je perds juste mon temps? Y a-t-il une meilleure façon? 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 déteste réinventer la roue quand ce n'est pas nécessaire.

Merci à tous ceux qui lisent, et surtout merci pour vos pensées si vous décidez de les quitter. J'ai essayé de chercher une réponse établie à cela, mais tout ce que je semble trouver, ce sont des questions/pages Web concernant l'utilisation d'un ListView INSIDE d'un fragment; n'utilisant pas les fragments en tant qu'éléments d'un ListView

Edit: j'ai pris les suggestions ci-dessous et j'ai commencé à enquêter davantage. D'après la façon dont les choses apparaissent, je devrais être en mesure d'utiliser un adaptateur personnalisé qui gonfle les fragments au lieu de simplement construire à partir de XML (faute d'un meilleur moyen de décrire le processus). Cependant, mon implémentation actuelle lance un NPE lorsque j'essaie de définir l'adaptateur.

Voici mon code d'adaptateur personnalisé (abrégé par 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 l'implémente:

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

Cela fait donc quelques jours et je suis presque sûr que ce fil a été enterré. Cependant, je pensais que j'ajouterais une dernière mise à jour au cas où quelqu'un voudrait l'essayer et qu'une recherche Google les amènerait ici. Donc, dans mon implémentation, j'obtiens un NPE lorsque ListView reçoit l'adaptateur. Il ne faut pas un chirurgien pour comprendre que c'est certainement l'adaptateur et non le ListView qui lance l'erreur. Pour ma vie, je n'arrive pas à comprendre pourquoi ...

Quoi qu'il en soit, je pense avoir une idée. Tout d'abord, une petite histoire: il y a quelque temps, j'essayais de faire des FragmentTransactions à l'intérieur d'un FragmentDialog. Chaque fois que j'essayais de le faire, j'obtenais un NPE. Finalement, grâce à de nombreuses recherches, j'ai découvert que la raison se rapportait à la façon dont les fragments sont instanciés. Lorsqu'un fragment est appelé, il a besoin du contexte de son parent. Étant donné que le parent d'un dialogue est l'activité qui l'a démarré, le dialogue lui-même ne répondait pas aux critères nécessaires. Je crois que lorsque vous essayez d'ajouter des fragments à un ListView, c'est également le cas. Étant donné que le ListView ne respecte pas l'accord avec l'instanciation d'un fragment, il jette le NPE et donc, me laisse accrocher et revenir aux conventions. D @ mn ... J'avais vraiment espéré pouvoir le faire. L'utilisation de fragments au lieu du simple XML aurait facilité l'organisation et la recherche dans la liste. Eh bien ... je suppose que cela ne peut pas être fait au cas où quelqu'un se demanderait.

39
user1449018

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

Un fragment est vraiment un sous-ensemble d'une activité. Auriez-vous jamais une activité dans une liste? Certainement pas (devrait être la réponse!) !!!

De plus, il n'est pas très utile d'attacher () et de détacher () un fragment en continu à mesure qu'ils se déplacent dans et hors de la vue (les cellules sont recyclées). Ce sont toutes des opérations coûteuses qu'un ListView ne devrait pas gérer. Les listes doivent défiler rapidement.

De la conversation sur les commentaires, je peux voir que vous voulez obtenir du code Nice avec une bonne séparation du code de configuration de la vue et de l'adaptateur dans l'activité. Faites-le avec:

  1. Remplacez la classe View et faites votre dessin et votre configuration personnalisés là-bas.
  2. Créez une nouvelle classe, dans laquelle vous fournissez un contexte et un ensemble de données nécessaires pour vous faire revenir la vue qu'une liste doit montrer - c'est ce que je fais habituellement.
  3. Ayez une classe d'Utils pour construire votre vidéo ailleurs (idiot).

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

8
dineth

Il s'avère que vous pouvez créer un ListView où chaque élément dans la listView est un Fragment. L'astuce consiste à envelopper le fragment dans un FrameLayout.

MISE À JOUR 16/09/2014

Même s'il est possible de créer un ListView contenant Fragments, il ne semble pas que ce soit une bonne idée. Cela semble définitivement être un cas d'angle dans le monde Android et il y a des dragons. Pour un fragment simple comme celui de l'exemple ci-dessous, tout fonctionne à merveille, mais si vous avez un projet complexe avec un il se passe beaucoup de choses alors ce n'est probablement pas la voie à suivre. Ma nouvelle approche consiste à extraire tout le code associé à l'interface graphique dans un View qui étend FrameLayout, et l'insérer dans un ListView - cela fonctionne BEAUCOUP MIEUX et correspond mieux à la façon dont Android s'attend à être utilisé. Si vous avez besoin des fonctionnalités d'un Fragment dans d'autres parties de votre code, vous pouvez simplement utiliser ce nouveau View là aussi.

Retour à la réponse d'origine ...

J'ai ajouté un nouvel exemple ManyFragments à mon exemple d'application AnDevCon 14 Fragments 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;
        }
    };

Si 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
JohnnyLambada

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

Écrivez un ViewAdapter personnalisé et faites-le gonfler une mise en page plus complexe (ou peut-être plusieurs mises en page plus complexes si vous avez vraiment besoin de fantaisie), puis remplissez les champs de la mise en page si nécessaire.

[En plus: aux personnes qui ont répondu dans les commentaires - veuillez utiliser des réponses plutôt que des commentaires si vous répondez réellement à la question! Ne serait-ce que parce que vous obtenez plus de points de réputation de cette façon!]

4
Dale Wilson