4

I try to setup following navigation in my app:

Actual condition: ViewPager + Tabs to swipe between lists:

  • ListFragment A
  • ListFragment B

Desired condition: ViewPager + Tabs:

  • ListFragment A onListItemSelected replace ListFragment A with DetailFragment A
  • ListFragment B onListItemSelected replace ListFragment B with DetailFragment B

The goal is to display the detail fragments inside the tab navigation. I can't replace the fragmentList by a detailFragment (the fragmentList has no custom layout and therefore no ID AND i don't know how to do it). Also, starting a new activity hides the tab bar.

Can someone please help me?

0xPixelfrost
  • 10,244
  • 5
  • 39
  • 58

1 Answers1

14

I would make a wrapper fragment which knows what to show - list or details. This would be completely decoupled from ViewPager - pager would only know it holds wrapper fragments, and these would manage their content themselves.

Implementation would be along these lines:

public class WrapperFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MyListFragment list = new MyListFragment();
        list.setListAdapter(adapter);
        list.setOnItemClickListener(new OnItemClickListener() {
            @Override public void onItemClick(AdapterView<?> l, View v, int position, long id) {
              // Create details fragment based on clicked item's position
              Fragment details = new Fragment();
              getChildFragmentManager()
                  .beginTransaction()
                  .replace(R.id.container, details)
                  .addToBackStack(null)
                  .commit();
            }
        });

        getChildFragmentManager()
            .beginTransaction()
            .add(R.id.container, list)
            .commit();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // There has to be a view with id `container` inside `wrapper.xml`
        return inflater.inflate(R.layout.wrapper, container, false);
    }

    public class MyListFragment extends ListFragment {

        private OnItemClickListener listener;

        public void setOnItemClickListener(OnItemClickListener l) {
            this.listener = l;
        }

        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            if(listener != null) {
              listener.onItemClick(l, v, position, id);
            }
        }
    }
}

wrapper.xml. Idea is that WrapperFragment has to provide a layout which contains a view with id container - because we're using view with this id to put child fragment - MyListFragment or DetailsFragment.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

Another way. This should work, but you'll have to try that out (layout has id itself, rather than having a child with id container):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
mindeh
  • 1,808
  • 14
  • 12
  • Would the layout wrapper look like this? I get an InflateException for onCreateView. – 0xPixelfrost Mar 10 '13 at 12:22
  • 1
    Added example for `wrapper.xml`. Let me know if you're having problems with this – mindeh Mar 10 '13 at 12:48
  • What would be the fastest way to get back from the detail fragment to the list/wrapper? – 0xPixelfrost Mar 10 '13 at 15:50
  • 1
    `getChildFragmentManager().popBackStack()`. Notice `.addToBackStack(null)` line when displaying `DetailsFragment`. It basically says "save this transaction as a history action - one such that can be moved back from by pressing HW back button". And `popBackStack()` kinda emulates HW back button click. It may be easier to understand `backStack` management by thinking of it as similar to HTML5 History API. – mindeh Mar 10 '13 at 19:53
  • Ah, and so when you do `popBackStack()`, you return to previous state - in your case that's `ListFragment`. – mindeh Mar 10 '13 at 19:55
  • 1
    firstly thanks for this, its solved an issue ive been having for the past few days, secondly regarding the backstack stuff i found that changing from getChildFragmentManager() to the regular FragmentManager() from support.app enabled the back button to work without manual calls from anywhere, using the childmanager did not, not sure what side effects this will have but its all behaving how i want :D – fury-s12 Mar 12 '13 at 03:35
  • I want same thing when clicked on a button in "Fragment A" (not listfragment). Any Idea ? – user2260168 Jun 24 '13 at 11:54
  • +1 Wrapper works great! But do you know why the onCreateOptionsMenu doesn't work anymore? I need it only for my listFragment – Kevin van Mierlo Nov 22 '13 at 16:19
  • 1
    @Kevin, sorry, missed your the comment. Reason might be that `onCreateOptionsMenu()` is called only on top-level fragment - wrapper. Solution could be to create menu from there - e.g. by calling `listFragment#onCreateOptionsMenu()` manually. That's from top of the head, you'd need to try this out – mindeh Nov 29 '13 at 09:15
  • @mindeh Thank you for still responding, I already found this answer and it is the same answer you gave me. It works great! – Kevin van Mierlo Nov 29 '13 at 10:02