Can a ListView contain Fragments
Asked Answered
R

3

40

As in, can the ELEMENTS of a ListView be Fragments. I know that you can assign a TextView XML to a ListView to change the way it looks, but can you add Fragments into a ListView.

For instance: I have a Fragment. The XML for said Fragment contains an ImageView, a couple of large-style TextViews, and a small-style TextView. The Fragment class code receives a Bundle, then based on the contents populates the TextViews and ImageView accordingly. Both the Fragment XML and the Fragment code work without issue (I can display an individual Fragment just fine). I have a FragmentActivity in which I want to display the aforementioned list of Fragments. Here is the code I'm using to try to populate the ListView inside of the FragmentActivity's View:

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

Here's my mode of thinking on this. I make an ArrayList of Fragments to hold all of my instantiated Views. I then create a Fragment, create a Bundle, add data to the Bundle (so that the Fragment can marshal data into it's Views correctly), add the Bundle to the Fragment, then finally add the Fragment to the ArrayList. After that, I make an ArrayAdapter, add the element layout I want to use, and the list of Fragments I've made; then set the ListView to read from my adapter.

Anyone running this code will likely get the NPE @ instantiating the ArrayAdapter. What gives? Is this even possible? Before I keep racking my brain on this can someone tell me if I'm just wasting my time? Is there a better way? I've been thinking of using a ScrollView, but so much of the functionality of a ListView would need to re-implemented and I hate-hate-hate reinventing the wheel when it's not necessary.

Thanks to anyone reading, and especially thank you for your thoughts if you decide to leave them. I've tried searching around for an established answer to this but all I seem to find are questions/web pages concerning using a ListView INSIDE of a Fragment; not using Fragments AS THE ELEMENTS of a ListView

Edit: I took the suggestions below and started investigating more. From the way things appear I should be able to use a custom adapter that inflates fragments instead of just flat out building from XML (for lack of a better way to describe the process) However, my current implementation is throwing an NPE when trying to set the adapter.

Here is my custom adapter code (shortened for brevity):

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

}

and here is how I'm implementing it:

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

So it's been a few days and I'm pretty sure this thread has been buried. However, I thought I would add one last update just in case someone wants to try this and a google search brings them here. So in my implementation I'm getting an NPE when the ListView is given the adapter. It doesn't take a rocket surgeon to figure out that it's certainly the adapter and not the ListView throwing the error. For the life of me I can't figure out why though...

At any rate, I think I have some idea though. First, a little back story: A while back I was trying to make FragmentTransactions inside of a FragmentDialog. Everytime I attempted to do so, I would get an NPE. Eventually, through much research, I discovered that the reason pertained to the way that Fragments are instanced. When a Fragment is called it needs the context from it's parent. Since a Dialog's parent is the Activity that started it, the Dialog itself didn't meet the criteria necessary. I believe, that when attempting to add fragments to a ListView, this is also the case. Since the ListView doesn't meet the agreement with instancing a Fragment it throws the NPE and thus, leaves me hanging and going back to conventions. [email protected] had really hoped I would be able to do this. Using Fragments instead of simple XML would have made it so much easier to organize/search through the list. Oh well... guess it can't be done in case anyone is wondering.

Ruche answered 6/7, 2012 at 13:38 Comment(11)
Why do you even want fragments to be "ELEMENTS of a ListView"? Developers have been creating ListViews for years without having their elements be fragments. What are you expecting to gain from making them be fragments?Tellus
Well, for one I expect to be able to show more than just a simple TextView inside of a list (in fact I'll be able to show an image, and a few extra lines of text). Also, I'll be able to reuse the fragment code over and over from different activities without duplicating code. I've also seen scrolling lists of items that are more dynamic than just a simple TextView (like the lists in Google Play, they are more complex than just a TextView). How is that achieved? Wouldn't that same effect be possible (an easier) by using a Fragment?Ruche
You can do that by using custom Adapter and Views inflated from XML for your rows.Matchlock
I like this alternative, but it puts all my code for instantiating those views in the activity hosting the ListView. If those Views for infalted in a fragment, then all that code is in the fragment. Do you have an example of the custom adapter that would handle this? Could I extend adapter in it's own class, thus keeping all the code for instancing these views in one spot?Ruche
"Wouldn't that same effect be possible (an easier) by using a Fragment?" -- well, considering nobody I know of is using fragments as elements in a ListView, it certainly is unlikely to be easier. Doing it without fragments borders on the trivial. Now, it is certainly conceivable that you could cook up a ListAdapter that is backed by fragments, by perhaps examining the similar relationship between PagerAdapter and FragmentPagerAdapter. However, you're breaking new ground, and it's unclear that is needed.Tellus
"it puts all my code for instantiating those views in the activity hosting the ListView" -- not necessarily, though that is one possible implementation. "Could I extend adapter in it's own class, thus keeping all the code for instancing these views in one spot?" -- you have to "extend adapter in it's own class" if you want ListView rows of the complexity that you describe.Tellus
I understand. Thanks for your input! I'm going to look into creating a ListAdapter and see how that works out. Part of the reason I'm so dead set on using Fragments is because that said Fragment gets used in multiple locations throughout the application. Anything that keeps me from duplicating code is worth the extra lifting IMHO. I'll update with what I find out :-) If this is indeed virgin territory then I will go bravely (and arguably, stupidly) forward. Yes...I like using fragments THAT much... Thanks again!!!Ruche
I've edited the OP to show progress.Ruche
If you found out that it is not possible, you might want to create an answer and mark it as accepted. This would help people who are looking for the same (most of them don't read the question as they suspect it wouldn't contain the answer).Germayne
+1 for the innovative attempt!List
RecyclerView can contain fragments. I had a problem that fragment in RecyclerView showed only for the first item. And I solved the problem. Just set new unique id to your container layout and you will able to add any fragment to you recyclerview item. This answer helped me. https://mcmap.net/q/408848/-fragment-replacing-in-recyclerview-item. For example, myContainerLayout.setId(SystemClock.currentThreadTimeMillis().toInt())Edaedacious
H
8

I'd say this is not possible to do as putting a fragment in a ListView would mean the fragment can be multiplied across multiple containers. When you use the FragmentManager to create a fragment, it is tagged with an identifier, making it simple to reload and rearrange on orientation and other configuration changes. It also encourages uses across multiple device configs.

A Fragment is really a subset of an Activity. Would you ever have an Activity as part of a list? Definitely not (should be the answer!)!!!

Moreover, it is not very useful to attach() and detach() a fragment continuously as they move in and out of view (cells get recycled). These are all expensive operations that a ListView shouldn't deal with. Lists should scroll quickly.

From the conversation on the comments, I can see you want to achieve nice code with a good separation of view setup code and adapter in the Activity. Do so with either:

  1. Override the View class and do your custom drawing and setup there.
  2. Create a new class, in which you supply a context and data set required for it to get you back the view a list needs to show - this is what I usually do.
  3. Have a Utils class to build your video elsewhere (silly).

Just don't use Fragments in Lists. Not the use case they are aiming for. HTH.

Hoenack answered 16/1, 2013 at 23:53 Comment(0)
L
7

It turns out that you can create a ListView where each item in the listView is a Fragment. The trick is wrapping the Fragment in a FrameLayout.

UPDATE 9/16/2014

Even though it is possible to create a ListView that contain Fragments, it doesn't look like it's a good idea. This seems to definitely be a corner case in the Android world and there be dragons. For a simple fragment like the one in the example below everything works beautifully, but if you have a complex project with a lot going on in it then this is probably not the way to go. My new approach is to pull all of the GUI related code into a View that extends FrameLayout, and insert that into a the ListView -- this works MUCH BETTER and is more in line with how Android expects to be used. If you need the functionality of a Fragment in other parts of your code, you can simply use this new View there too.

Back to the original answer...

I've added a new ManyFragments example to my AnDevCon 14 Fragments example app if you want to try it out. Essentially it comes down the the BaseAdapter, which in my example looks like this:

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

In case you're curious here's 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);
Liesa answered 15/9, 2014 at 19:16 Comment(1)
Can you guide me how i am suppose to create different layout for each list itemDavidoff
J
4

You don't need to use Fragments.

Write a custom ViewAdapter and have it inflate a more complex layout (or maybe several more complex layouts if you need to get really fancy) then populate the fields of the layout as necessary.

[Aside: to the people who answered in comments -- please use answers rather than comments if you are actually answering the question! If only because you get more reputation points that way!]

Julee answered 19/3, 2013 at 20:46 Comment(1)
PS: If you are trying to reuse parts of one layout in another, take a look at the <include layout="@layout/partial_view" /> elementJulee

© 2022 - 2024 — McMap. All rights reserved.