skip to Main Content

Right now I have a tablayout with a ViewPager + adapter below it. I want to pause a fragment when a different tab is selected (not destroy/pop it, just pause it) and resume it when the fragment’s tab is selected.

public class PagerFragment extends Fragment {

    ViewPagerAdapter pagerAdapter;
    ViewPager pager;
    TabLayout tabLayout;
    int position;

    public PagerFragment() {
        super();
    }

    public void setPosition(int position) {
        this.position = position;
    }

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

    @Override
    public void onStop() {
        super.onStop();
    }

    @Override
    public void onPause() {
        super.onPause();
        pagerAdapter.clear();
        pagerAdapter.notifyDataSetChanged();
    }

    @Override
    public void onStart() {
        super.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!isAdded()) {
            pagerAdapter.instantiateItem(pager, position);
        }
    }

    @SuppressLint("InflateParams")
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_pager, null);
        pagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
        setPosition(position);
        pagerAdapter.setDevicesList(devicesList);
        pager = root.findViewById(R.id.pager);
        pager.setAdapter(pagerAdapter);
       
        tabLayout = root.findViewById(R.id.tab_layout);

        for (int i = 0; i < devicesList.size(); i++) {
            tabLayout.addTab(tabLayout.newTab().setText(String.valueOf(devicesList.get(i).getName())));
        }

        TabLayout.Tab tab = tabLayout.getTabAt(position);
        if (tab != null) {
            tab.select();
            pager.setCurrentItem(position);
            tabLayout.setupWithViewPager(pager);

            tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    pager.setCurrentItem(tab.getPosition());
                }

                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
                    int pos = tab.getPosition();
                }

                @Override
                public void onTabReselected(TabLayout.Tab tab) {

                }
            });
        }
        tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(pager));
        return root;
    }
}



public class ViewPagerAdapter extends FragmentPagerAdapter  {


    ArrayList<ConnectedBluetoothDevice> devicesList;
    ArrayList<DataViewFragment> dataViewFragments;

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

    /**
     * Initializes the adaptor with the device list and the list of data fragments.
     * @param devicesList List of devices to be displayed in their own data fragment.
     */
    public void setDevicesList(ArrayList<ConnectedBluetoothDevice> devicesList) {
        this.devicesList = devicesList;
        dataViewFragments = new ArrayList<>();
        for (int i = 0; i < devicesList.size(); i++) {
            dataViewFragments.add(new DataViewFragment(devicesList.get(i)));
        }
    }

    /**
     * GetItem
     * @param position position of item in the viewPager list to be returned
     * @return fragment to view
     */
    @NonNull
    public Fragment getItem(int position) {
        if (dataViewFragments != null && position < dataViewFragments.size()) {
            return dataViewFragments.get(position);
        } else {
            return new DataViewFragment(null);
        }
    }

    @Override
    public int getCount() {
        return devicesList.size();
    }

    /**
     * Updates the tabs in tabLayout while swiping pages
     * @param position of the viewPage
     * @return title of the tab
     */
    public CharSequence getPageTitle(int position) {
        super.getPageTitle(position);
        return devicesList.get(position).getName();
    }

    public void clear() {
        dataViewFragments.clear();
    }
}

Some notes:

  • I’m instructed to fix this bug in ViewPager and FragmentPagerAdapter, so please don’t recommend me solutions in ViewPager2 and FragmentStatePagerAdapter 🙂
  • I heard that setOffscreenPageLimit(0); isn’t the best practice so I want to avoid that situation (especially since I only need to pause the fragment but not destroy it)

2

Answers


  1. This depends on the version of ViewPager you are using as it was added later.

    Your are using a deprecated constructor

    So as long as your using a recent version of Viewpager you should use the non deprecated constructor:-

    public FragmentPagerAdapter(
        @NonNull FragmentManager fm,
        @FragmentPagerAdapter.Behavior int behavior
    )
    

    This constructor with the behaviour BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

    then only the current Fragment is in the RESUMED state. All other fragments are capped at STARTED

    To get to the STARTED lifecycle state from RESUMED onPause is called.

    so change

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

    to

    public ViewPagerAdapter(FragmentManager fm) {
                super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
            }
    
    
    Login or Signup to reply.
  2. This method is used to for calling functions to run only when the specified tab is selected.

     @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
    
        if (isVisibleToUser) {
          // add your tab functions here
        } else {
            // do nothing
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search