Context:

I'm making a music player and I'm currently working on the miniplayer. The miniplayer lives in a ViewPager along with a floating action button (FAB) for shuffle. Initially, only the FAB is seen on screen. When you click on the FAB, music playback starts, the Fab-Miniplayer ViewPager's page count goes from 1 to 2, and its page is set to the second page, where the miniplayer is. The miniplayer itself in the second page is a vertical viewpager, so one can swipe through to different songs. The miniplayer/vertical viewpager is backed by a PagerAdapter that gets songs from a SongQueue obejct.

The Problem:

If I click on the FAB once, the miniplayer opens and everything works. Then I dismiss the miniplayer to stop playback by swiping back to the FAB page in the ViewPager. After clicking on the FAB to start playback and open the miniplayer for the second time, the first two fragments are never created. The miniplayer vertical ViewPager's PagerAdapter's getItem() methods are never called for item 0 and item 1. On a higher level, the first two songs in the miniplayer aren't showing up. Upon further investigation, the onCreate() methods are being called for the first two songs from the last time the miniplayer was opened, so I'm guessing the ViewPager is implementing some kind of caching or a reference is being held onto for too long because the miniplayer vertical viewpager thinks that it already has the first two songs the second time it is instantiated. Hopefully this problem description makes sense. If there are any questions, I'd be happy to try and go into more detail.

The Code

SongsFragment

/** * Fragment used to display all of the songs on the device. */ public class SongsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { private static final String TAG = "SongsFragment"; private SwipeListView mSwipeListView; private ViewPager fabMiniPlayerViewPager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); setHasOptionsMenu(true); // Initialize the loader to load the list of songs getLoaderManager().initLoader(SongCursorLoader.ALL_SONGS_ID, null, this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.list_view_songs, parent, false); if (view != null) { mSwipeListView = (SwipeListView) view.findViewById(R.id.list_view_songs); } LinearLayout emptyView = (LinearLayout) inflater.inflate(R.layout.list_view_songs_empty, parent, false); mSwipeListView.setEmptyView(emptyView); fabMiniPlayerViewPager = (ViewPager) view.findViewById(R.id.fab_miniplayer_ViewPager); Integer viewPagerPageCount = new Integer(1); fabMiniPlayerViewPager.setTag(viewPagerPageCount); // The fabMiniPlayerViewPager gets it's page count from the tag so it can be dynamically modified fabMiniPlayerViewPager.setAdapter(new FragmentStatePagerAdapter(getChildFragmentManager()) { @Override public Fragment getItem(int i) { if (i == 0) { // ShuffleFabFragment just houses a single button, the code is below ShuffleFabFragment fragment = new ShuffleFabFragment(); fragment.setViewPager(fabMiniPlayerViewPager); return fragment; } MiniPlayerFragment fragment = new MiniPlayerFragment(); return fragment; } @Override public int getCount() { Integer count = (Integer) fabMiniPlayerViewPager.getTag(); return count.intValue(); } }); fabMiniPlayerViewPager.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { // Pass touch events to the list view behind it mSwipeListView.onTouchEvent(motionEvent); return false; } }); return view; } @Override public void onResume() { super.onResume(); if (!MusicPlayerService.isStopped()) { Integer viewPagerPageCount = 2; fabMiniPlayerViewPager.setTag(viewPagerPageCount); PagerAdapter adapter = fabMiniPlayerViewPager.getAdapter(); fabMiniPlayerViewPager.setAdapter(adapter); fabMiniPlayerViewPager.setCurrentItem(1, false); } else { Integer viewPagerPageCount = 1; fabMiniPlayerViewPager.setTag(viewPagerPageCount); fabMiniPlayerViewPager.getAdapter().notifyDataSetChanged(); } } }

ShuffleFabFragment

/** * Fragment representing the Shuffle Floating Action Button. On FAB click, it turns on shuffle and * plays a random song. Depending on the user preference, the mini-player will appear or the * Now Playing view will appear. The ShuffleFabFragment holds a reference to its containing * ViewPager so it can do the following: * <p/> * 1. Add the MiniPlayer fragment to the ViewPager to enable swiping & animations * 2. Remove the MiniPlayer fragment when it is swiped away * <p/> * Once the MiniPlayer is swiped away (to the right), it stops music playback. */ public class ShuffleFabFragment extends Fragment { private static final String TAG = "ShuffleFabFragment"; private ViewPager fabMiniPlayerViewPager; public void setViewPager(ViewPager viewPager) { fabMiniPlayerViewPager = viewPager; fabMiniPlayerViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int i, float v, int i2) { } @Override public void onPageSelected(int i) { if (i == 0) { // MiniPlayer swiped away Integer viewPagerPageCount = new Integer(1); fabMiniPlayerViewPager.setTag(viewPagerPageCount); PagerAdapter adapter = fabMiniPlayerViewPager.getAdapter(); Intent stopMusicIntent = new Intent(getActivity(), MusicPlayerService.class); stopMusicIntent.setAction(MusicPlayerService.ACTION_STOP); getActivity().startService(stopMusicIntent); fabMiniPlayerViewPager.setAdapter(adapter); } } @Override public void onPageScrollStateChanged(int i) { } }); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.shuffle_fab, viewGroup, false); ImageView shuffleFabImageView = (ImageView) view.findViewById(R.id.shuffle_fab_ImageView); shuffleFabImageView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: // Scale up on touch to make the button appear to come closer view.setScaleX(8f / 7f); view.setScaleY(8f / 7f); break; case MotionEvent.ACTION_UP: view.setScaleX(1f); view.setScaleY(1f); break; default: view.setScaleX(1f); view.setScaleY(1f); } return false; } }); shuffleFabImageView.setClickable(true); shuffleFabImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String shuffleMode = PreferencesHelper.getShuffleMode(getActivity()); if (shuffleMode.equals(PreferencesHelper.SHUFFLE_MODE_OFF)) { PreferencesHelper.setShuffleMode(getActivity(), PreferencesHelper.SHUFFLE_MODE_SMART); } SongQueue.initializeQueue(null, true, Song.COLLECTION_TYPE_ALL_SONGS, getActivity().getApplicationContext(), null); Song firstSong = SongQueue.getSong(0); firstSong.play(getActivity(), false); // On Click, update the page count, then set page to miniplayer Integer viewPagerPageCount = new Integer(2); fabMiniPlayerViewPager.setTag(viewPagerPageCount); fabMiniPlayerViewPager.getAdapter().notifyDataSetChanged(); fabMiniPlayerViewPager.setCurrentItem(1, true); } }); return view; } }

MiniPlayerFragment

/** * Hosts MiniplayerCardFragments and allows for song skipping */ public class MiniPlayerFragment extends Fragment implements ViewPager.OnPageChangeListener { VerticalViewPager miniplayerCardViewPager; @Override public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.view_pager_miniplayer, viewGroup, false); miniplayerCardViewPager = (VerticalViewPager) view.findViewById(R.id.miniplayer_cards_ViewPager); miniplayerCardViewPager.setAdapter(new FragmentStatePagerAdapter(getChildFragmentManager()) { @Override public Fragment getItem(int position) { Song song = SongQueue.getSong(position); return MiniPlayerCardFragment.newInstance(song); } @Override public int getCount() { return SongQueue.getTotalSize(); } }); miniplayerCardViewPager.setOnPageChangeListener(this); SongQueue.addOnQueueChangeListener(new SongQueue.OnQueueChangeListener() { @Override public void onNextSongChanged() { onQueueChanged(); } @Override public void onQueueChanged() { // Force re-layout to update fragments int queuePosition = SongQueue.getQueuePositionCurrentSong(); PagerAdapter adapter = miniplayerCardViewPager.getAdapter(); miniplayerCardViewPager.setAdapter(adapter); miniplayerCardViewPager.setCurrentItem(queuePosition, true); } @Override public void onCurrentSongChanged() { onQueueChanged(); } }); return view; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { int queuePosition = SongQueue.getQueuePositionCurrentSong(); if (queuePosition < position) { Intent nextIntent = new Intent(getActivity(), MusicPlayerService.class); nextIntent.setAction(MusicPlayerService.ACTION_NEXT); getActivity().startService(nextIntent); } else if (queuePosition > position) { Intent prevIntent = new Intent(getActivity(), MusicPlayerService.class); prevIntent.setAction(MusicPlayerService.ACTION_PREV); getActivity().startService(prevIntent); } } @Override public void onPageScrollStateChanged(int state) { } @Override public void onResume() { super.onResume(); // Force re-layout to update fragments int queuePosition = SongQueue.getQueuePositionCurrentSong(); PagerAdapter adapter = miniplayerCardViewPager.getAdapter(); miniplayerCardViewPager.setAdapter(adapter); miniplayerCardViewPager.setCurrentItem(queuePosition, true); SongQueue.setPagerAdapter(adapter); } }

Screenshots to help visualization