Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fragment in ViewPager is not displaying anything in its RecyclerView on rotation

I have 4 Fragments in a ViewPager that is integrated with a TabLayout. Each of those Fragments holds a RecyclerView because I'm displaying an unknown amount of list items. The items are loaded by date, so I have two buttons that let you change days, and then the appropriate data in the list items is loaded based on the date that is set. The buttons are part of the outer activity.

Everything so far works perfectly BUT once I rotate the device the fragments don't load anything. Nothing is displayed inside the Fragments. The views in the outer activity are displayed fine though. The weird thing is that if I touch one of the buttons that changes the date, the Fragments are loaded and the data is displayed correctly. But I need everything to be displayed immediately after rotation not just when I click one of those buttons. Here is my code:

public class MainActivity extends AppCompatActivity implements DatePickerFragment.DateDialogInterface
{
    private final static String COLOR_CONTROL_CHOSEN = "color_control_chosen";
    private static final String DIALOG_DATE = "dialog_date";
    private static final String TAG = "MainActivity";
    private static final String SET_LOCATION = "set_location";
    public static final String LEAGUE_POSITION = "league_position";
    private Toolbar mToolbar;
    private ViewPager mViewPager;
    private TabLayout mTabLayout;
    private ImageButton mPrevDateButton, mNextDateButton;
    private TextView mDateTextView, mLeagueTextView;
    private String mTodaysDate;
    private Date mLastSelectedDate, mTodaysDateObj;
    private ProgressBar mProgressBar;
    private FragmentPagerAdapter mAdapterViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mToolbar = (Toolbar) findViewById(R.id.outnix_toolbar);
        mViewPager = (ViewPager) findViewById(R.id.main_activity_view_pager);
        mTabLayout = (TabLayout) findViewById(R.id.main_activity_tab_layout);
        mDateTextView = (TextView) findViewById(R.id.display_date_text_view);
        mLeagueTextView = (TextView) findViewById(R.id.league_title_header);
        mPrevDateButton = (ImageButton) findViewById(R.id.previous_date_button);
        mNextDateButton = (ImageButton) findViewById(R.id.next_date_button);
        mProgressBar = (ProgressBar) findViewById(R.id.loading_circle);
        mProgressBar.setVisibility(View.GONE);
        setTodaysDate();
        mDateTextView.setText("Today");

        setSupportActionBar(mToolbar);
        assert getSupportActionBar() != null;
        getSupportActionBar().setDisplayShowTitleEnabled(false);

        NavDrawerFragment navDrawerFrag = (NavDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_nav_drawer);
        navDrawerFrag.setUp((DrawerLayout) findViewById(R.id.nav_drawer), mToolbar);

        setUpViewPagerAdapterOnCreate();
        mTabLayout.setupWithViewPager(mViewPager);

        mDateTextView.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                FragmentManager fm = getSupportFragmentManager();
                DatePickerFragment dialog = new DatePickerFragment();
                Bundle args = new Bundle();
                args.putSerializable("ARG_DATE", mLastSelectedDate);
                dialog.setArguments(args);
                dialog.show(fm, DIALOG_DATE);
            }
        });

     THESE TWO LISTENERS ARE WHAT DISPLAY THE DATA AFTER A ROTATION CHANGE
     //////////////////////////////////////////////////////////////////
        mPrevDateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v)
            {
                mProgressBar.setVisibility(View.VISIBLE);
                updateDate(-1);
                setupViewPagerAdapter(getViewPagerPos());
            }
        });

        mNextDateButton.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                mProgressBar.setVisibility(View.VISIBLE);
                updateDate(1);
                setupViewPagerAdapter(getViewPagerPos());
            }
        });
    }
    ////////////////////////////////////////////////////////////////////
/*  @Override
    protected void onResume()
    {
        super.onResume();
        setupViewPagerAdapter(getViewPagerPos());
    }*/

    private void setUpViewPagerAdapterOnCreate()    //only called when opened from LauncherActivity
    {
        setupViewPagerAdapter(0);

        int touchColorPixel = getIntent().getIntExtra(COLOR_CONTROL_CHOSEN, ContextCompat.getColor(this, R.color.blue));

        if(touchColorPixel == ContextCompat.getColor(this, R.color.orange))
            mViewPager.setCurrentItem(0);
        else if(touchColorPixel == ContextCompat.getColor(this, R.color.blue))
            mViewPager.setCurrentItem(1);
        else
            mViewPager.setCurrentItem(3);
    }

    private void setupViewPagerAdapter(int currentPos)
    {
        mAdapterViewPager = new MainPagerAdapter(getSupportFragmentManager());

        mViewPager.setAdapter(mAdapterViewPager);
        mViewPager.setOffscreenPageLimit(4);
        mViewPager.setCurrentItem(currentPos);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        super.onSaveInstanceState(outState);
        outState.putSerializable(DIALOG_DATE, mLastSelectedDate);           //save the last date selected (or last date displayed) on rotation change.
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState)
    {
        super.onRestoreInstanceState(savedInstanceState);
        setDateFromDialog((Date) savedInstanceState.getSerializable(DIALOG_DATE));  //restore the last date selected to the new rotation change
    }

    public ProgressBar getProgressBar()
    {
        return mProgressBar;
    }

    @Override
    public void setDateFromDialog(Date date)
    {
        String newDate = getFormattedDate(date);
        updateTodaysDate();     
        mLastSelectedDate = date;

        setupViewPagerAdapter(getViewPagerPos());

        if(newDate.equals(mTodaysDate))
            mDateTextView.setText("Today");
        else
            mDateTextView.setText(newDate);
    }

    private int getViewPagerPos()
    {
        return mViewPager.getCurrentItem();
    }
}

The 2 buttons mPrevDateButton and mNextDateButtonare the buttons that when clicked "fix" the problem. Inside their click listeners, the updateDate() method is only doing stuff related to a TextView object and is why I didn't include the definitions (I'm trying to keep the post as short as possible). So I know for sure it has something to do with my setupViewPagerAdapter() method. I've tried adding this method in onCreate() and onResume() but the Fragments still stay blank.

Here is my ViewPager adapter code:

public class MainPagerAdapter extends FragmentPagerAdapter
{
    private final String[] mFragmentTitleList = {"My Games", "All Games", "All Places", "My Places"};   //tab title names
    private final static int NUM_FRAGMENTS = 4;

    public MainPagerAdapter(FragmentManager fm)
    {
        super(fm);
    }

    @Override
    public Fragment getItem(int position)
    {
        switch(position)
        {
            case 0:
                return MyGamesFragment.newInstance();
            case 1:
                return AllGamesFragment.newInstance();
            case 2:
                return AllPlacesFragment.newInstance();
            case 3:
                return MyPlacesFragment.newInstance();
            default:
                return null;
        }
    }

    @Override
    public int getCount()
    {
        return NUM_FRAGMENTS;
    }

    @Override
    public CharSequence getPageTitle(int position)
    {
        return mFragmentTitleList[position];
    }
}

Here is one of the 4 fragments. They pretty much all have similar design.

public class AllGamesFragment extends Fragment
{
    private SwipeRefreshLayout mSwipeRefreshLayout;
    private RecyclerView mAllGamesRecyclerView;
    private GameAdapter mGameAdapter;
    private List<Game> mGameList = new ArrayList<>();
    private MainActivity mMainActivity;     //keeps a reference to MainActivity to access public methods

    public static AllGamesFragment newInstance()
    {
        return new AllGamesFragment();
    }

    @Override
    public void onAttach(Context context)
    {
        super.onAttach(context);
        mMainActivity = (MainActivity) context;
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        mMainActivity.getProgressBar().setVisibility(View.VISIBLE);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.recycler_view_all_games, container, false);
        mAllGamesRecyclerView = (RecyclerView) view.findViewById(R.id.all_games_recycler_view);
        mAllGamesRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
        {
            @Override
            public void onRefresh()
            {
                getGamesData();
                mSwipeRefreshLayout.setRefreshing(false);
            }
        });

        return view;
    }

    @Override
    public void onResume()
    {
        super.onResume();
        getGamesData();
    }

    private void setupAdapter()
    {
        if(isAdded())
        {
            mGameAdapter = new GameAdapter(mGameList);
            mAllGamesRecyclerView.setAdapter(mGameAdapter);
        }
    }

    private void getGamesData()
    {
        new GetDataTask(mMainActivity.getLastSelectedDate()).execute();
    }

    private class GetDataTask extends Scraper.GameDataTask
    {
        GetDataTask(Date date)
        {
            super(date, getActivity());
        }

        @Override
        protected void onPostExecute(List<Game> games)
        {
            mGameList = games;
            setupAdapter();
            mMainActivity.getProgressBar().setVisibility(View.GONE);
        }
    }

    private class GameAdapter extends RecyclerView.Adapter<GameHolder>
    {
        private List<Game> mGames;

        public GameAdapter(List<Game> games)
        {
            mGames = games;
        }

        @Override
        public GameHolder onCreateViewHolder(ViewGroup parent, int viewType)
        {
            LayoutInflater inflater = LayoutInflater.from(getActivity());
            View view = inflater.inflate(R.layout.list_game_item, parent, false);
            return new GameHolder(view, getActivity());
        }

        @Override
        public void onBindViewHolder(GameHolder holder, int position)
        {
            holder.bindGameData(mGames.get(position));
        }

        @Override
        public int getItemCount()
        {
            return mGames.size();
        }
    }

I've looked at so many posts on here, changed my code many times and I still can't get it to work. I'm just baffled that it "fixes" when I click one of the date buttons. If you would like me to post more code of something let me know. Sorry for the long post but I feel the context is needed. I really appreciate any help!

like image 712
David Velasquez Avatar asked Feb 08 '23 11:02

David Velasquez


2 Answers

I figured it out! I had to switch to a FragmentStatePagerAdapter rather than use a FragmentPagerAdapter. I believe it has something to do with the Fragments being destroyed and recreated again.

I also read that another way would be to wrap the ViewPager in a Fragment and when you create its adapter you use getChildFragmentManager() rather than getSupportFragmentManager() like this:

mAdapterViewPager = new MyPagerAdapter(getChildFragmentManager());

I didn't want to have to add an entire Fragment around my ViewPager just to fix this issue so I switched to using FragmentStatePagerAdapter and it worked! Hope this helps someone else out there.

like image 72
David Velasquez Avatar answered Feb 10 '23 01:02

David Velasquez


Making viewPager adapter extends FragmentStatePagerAdapter and not FragmentPagerAdapter should solve the problem.

like image 39
Victor Irechukwu Avatar answered Feb 10 '23 00:02

Victor Irechukwu