Get current fragment with ViewPager2

后端 未结 11 1847
野的像风
野的像风 2020-12-24 01:15

I\'m migrating my ViewPager to ViewPager2 since the latter is supposed to solve all the problems of the former. Unfortunately, when using it with a

11条回答
  •  时光说笑
    2020-12-24 01:24

    I had the same problem. I converted from ViewPager to ViewPager2, using FragmentStateAdapter. In my case I have a DetailActivity class (extends AppCompatActivity) which houses the ViewPager2, which is used to page through lists of data (Contacts, Media, etc.) on smaller form-factor devices.

    I need to know the currently shown fragment (which is my own class DetailFragment which extends androidx.fragment.app.Fragment), because that class contains the string I use to update the title on the DetailActivity toolbar.

    I first started down the road of registering an onPageChangeCallback listener as suggested by some, but I quickly ran into problems:

    1. I initially created tags during the ViewPager2's adapter.createFragment() call as suggested by some with the idea to add the newly created fragment to a Bundle object (using FragmentManager.put()) with that tag. This way I could then save them across config changes. The problem here is that during createFragment(), the fragment isn't actually yet part of the FragmentManager, so the put() calls fail.
    2. The other problem is that if the fundamental idea is to use the onPageSelected() method of the OnPageChangeCallback to find the fragment using the internally generated "f"+position tag names - we again have the same timing issue: The very first time in, onPageSelected() is called PRIOR to the createFragment() call on the adapter - so there are no fragments yet created and added to the FragmentManager, so I can't get a reference to that first fragment using the "f0" tag.
    3. I then tried to find a way where I could save the position passed in to onPageSelected, then try and reference that somewhere else in order to retrieve the fragment after the adapter had made the createFragment() calls - but I could not identify any type of handler within the adapter, the associated recyclerview, the viewpager etc. that allows me to surface the list of fragments that I could then reference that to the position identified within that listener. Strangely, for example, one adapter method that looked very promising was onViewAttachedToWindow() - however it is marked final so can't be overridden (even though the JavaDoc clearly anticipates it being used this way).

    So what I ended up doing that worked for me was the following:

    1. In my DetailFragment class, I created an interface that can be implemented by the hosting activity:
        public interface DetailFragmentShownListener {
            // Allows classes that extend this to update visual items after shown
            void onDetailFragmentShown(DetailFragment me);
        }
    
    1. Then I added code within onResume() of DetailFragment to see if the associated activity has implemented the DetailFragmentShownListener interface within this class, and if so I make the callback:
        public void onResume() {
            super.onResume();
            View v = getView();
            if (v!=null && v.getViewTreeObserver().isAlive()) {
                v.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        v.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                        // Let our parent know we are laid out
                        if ( getActivity() instanceof DetailFragmentShownListener ) {
                            ((DetailFragmentShownListener) getActivity()).onDetailFragmentShown(DetailFragment.this);
                        }
                    }
                });
            }
        }
    
    1. Then where I need to know when this fragment is shown (such as within DetailActivity), I implement the interface and when it receives this callback I know that this is the current fragment:
        @Override
        public void onDetailFragmentShown(DetailFragment me) {
            mCurrentFragment = me;
            updateToolbarTitle();
        }
    

    mCurrentFragment is a property of this class as its used in various other places.

提交回复
热议问题