How to get the instance of the currently visible fragment in ViewPager:

后端 未结 3 1442
一生所求
一生所求 2021-01-06 23:28

I\'m building an application that shows different fragments in a ViewPager. I add these fragments to the ViewPager like this:

 publ         


        
相关标签:
3条回答
  • 2021-01-06 23:50

    The way I've found the currently visible fragment is using setUserVisibleHint and an interface back to the ViewPager. This method is in the fragment class. Override it and use an interface to call back to your ViewPager. If the fragment is visible - i.e. it returns true - then just use a reference to your fragment (either from the list backing your adapter or one you stored as an instance variable) to get whatever you need out of the fragment itself. I've added the code below.

    Declare an interface related to whatever you want to do. In my case I was using this to disable the ViewPager default x-direction listener when a Google Map instance was visible so that the scrolling of the map did not trigger the change of fragment.

    public interface OnMapFragmentVisibleListener{
        public void mapVisible(boolean visible);
    }
    

    In the fragment:

    @Override 
    public void onAttach(Activity activity){ 
        super.onAttach(activity);
        try{
            mapFragmentVisibilityListener = (OnMapFragmentVisibleListener) activity;
            isAttached = true; //flag for whether this fragment is attached to pager
        } catch (ClassCastException e){
            throw new ClassCastException(activity.toString() + " must implement interface onAttach");
        }
    
    
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser){
        if(isVisibleToUser && isAttached){ //if listener is called before fragment is attached will throw NPE
            mapFragmentVisibilityListener.mapVisible(true);
        }
    }
    

    One thing that was not obvious until I tried this out was the addition of the isAttached variable. I also override the fragments onAttach method and set this to true once it is called. Otherwise what happens is your fragment will be visible to the user and try to call the interface to your ViewPager before the interface is initialized.

    In my ViewPager I just implement the OnMapFragmentVisibleListener and then add the required method

        @Override
    public void mapVisible(boolean visible) {
        if(visible)
        viewPager.allowSwipe(false); //turn off viewPager intercept touch if map visible
    }
    

    In my case I used it to turn off the ViewPager swipe function. However, I could have easily called back to a public method in my mapFragment. I happen to have 3 fragments in my ViewPagerthat I keep a reference to.

    Update

    To find the fragment that is currently displayed, you first need to find out which fragment is being displayed. You can do this a number of ways. The easiest is to pass some descriptor back to your ViewPager / FragmentActivity with the interface you created before.

    public interface OnFragmentVisibleListener{
        public void fragmentVisible(boolean true, String tag);
    }
    

    The problem with your setup is that you're using a Vector which is really a synchronized List to store your fragment references. As a result, there is no key value by which you can locate your fragments later. Fragments have the ability to be identified by a tag that is created during the fragment transaction when they are added to the fragment activity. However, in a ViewPager setup, you do not add the fragments individually and so can't add tags during the transaction.

    My solution has been to back my BasePagerAdapter with a HashMap in which I store each fragment identified by a unique key.

     LinkedHashMap<String, Fragment> tabs = new LinkedHashMap<String, Fragment>();
     tabs.put("Frag1", fragment1);
     tabs.put("Frag2", fragment2);
     tabs.put("Frag3", fragment3);
    

    Then I use this in the adapter:

    private class BasePagerAdapter extends FragmentPagerAdapter{
    
        private LinkedHashMap<String, Fragment> tabs;
    
        public BasePagerAdapter(LinkedHashMap<String, Fragment> tabMap, FragmentManager fm)
        {
            super(fm);
            this.tabs = tabMap;
        }
    
         // other basic methods
    

    Because your references are stored to key values, it's easy to find the fragment you're looking for because you can just search by key from the HashMap and return.

    If you cannot change the setup to get away from the Vector you need to either (1) keep track of which fragment you add in which order (assuming fragments are not added and removed dynamically) so that you can get it by index or (2) keep separate references to each fragment.

    private Fragment x;
    
    //in onCreate
    x = new Fragment();
    
    //in interface
    if(tag.equals("x"){
        x.doSomething();
    }
    

    Another thing that makes this easier is that my fragments are all sub-classed. E.g. each fragment that performs a different function has its own class. As there is only one instance of each class it is easy to find since I do not have any duplicates. If you're using all generic members of the Fragment class you just need to implement some system of tagging that makes one of the approaches above possible.

    0 讨论(0)
  • 2021-01-06 23:56

    You can get the Current Visible Fragment using viewpager.getCurrentItem() method in the following way:

    Fragment fr = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + mViewPager.getCurrentItem());
    
    0 讨论(0)
  • 2021-01-07 00:03

    The way I do it as of year 2020 in Kotlin:

    Inside the fragment class I add a public variable:

    var fragmentNo = 0
    

    The fragmentNo variable is filled through the adapter's getItem as follows (note fragment.fragmentNo = pos) in particular:

        private inner class ViewPagerAdapter(fragmentManager: FragmentManager)
            : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
    
            override fun getCount(): Int = list.size
            var position = 0
            lateinit var fragment: ExerciseFragment
    
            override fun getItem(pos: Int): Fragment {
                fragment = ExerciseFragment()
                position = pos
                val bundle = Bundle()
                bundle.putSerializable(Constants.WORD_ID, list[position])
                fragment.arguments = bundle
                fragment.fragmentNo = pos
                return fragment
            }
    
        }
    

    Afterwards, I use the visible fragment in a button clicker, which is outside the viewpager, as follows:

    btShowAnswer.setOnClickListener {
                Logger.print(this, "Show Answer Button clicked")
    
                val fr: ExerciseFragment? = supportFragmentManager.fragments.find {
                    it is ExerciseFragment && it.fragmentNo == viewPager.currentItem
                } as? ExerciseFragment
    
                fr?.showAnswer()
    
            }
    

    The below line finds the required fragment in particular:

    val fr: ExerciseFragment? = supportFragmentManager.fragments.find {
                    it is ExerciseFragment && it.fragmentNo == viewPager.currentItem
                } as? ExerciseFragment
    
    0 讨论(0)
提交回复
热议问题