如何在ViewPager中获得当前可见片段的实例。[英] How to get the instance of the currently visible fragment in ViewPager:

本文是小编为大家收集整理的关于如何在ViewPager中获得当前可见片段的实例。的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在构建一个显示A ViewPager中不同片段的应用程序.我将这些片段添加到ViewPager之类的ViewPager:

 public void intialiseViewPager() 
 {
    List<Fragment> fragments = new Vector<Fragment>();
    numberOfTabs = application.currentReport.getODTabsList().size();
    for (int i = 0; i < numberOfTabs; i++)      
    {
        ODTab tempTab = application.currentReport.getODTabsList().get(i);
        if (tempTab.getTabType().equals(ODGrid.XML_GRID_ELEMENT))
        {
            GridFragment gridFragment = GridFragment.newInstance(tempTab.getTabId());
            fragments.add(gridFragment);
        }
        else  if (tempTab.getTabType().equals(ODChart.XML_CHART_ELEMENT))
        {
            NewChartFragment chartFragment = NewChartFragment.newInstance(tempTab.getTabId());
            fragments.add(chartFragment);
        }
    }
    Log.d(TAG, "Current report fragments set to adapter: "+fragments.toString());
    mPagerAdapter  = new ViewPagerAdapter(getSupportFragmentManager(), fragments);
    mViewPager = (ViewPager)findViewById(R.id.pager);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setOffscreenPageLimit(0);
    mViewPager.setOnPageChangeListener(this);
}

您可以看到我传递到片段(tempTab.getTabId()),在此行中:

GridFragment gridFragment = GridFragment.newInstance(tempTab.getTabId());

在片段本身中,我这样做是为了初始化:

public static final GridFragment newInstance(String tabId)
{
    GridFragment f = new GridFragment();
    Bundle bdl = new Bundle(2);
    bdl.putString(TAB_ID, tabId);
    f.setArguments(bdl);
    return f;
}

@Override
public void onCreate(Bundle savedInstanceState) 
{
    String tabId = getArguments().getString(TAB_ID);
    if (application.currentReport != null)
    {
        this.odTab = application.currentReport.getODTabByTabId(tabId);
    }
    else
    {
        startActivity(new Intent(getActivity(), LoginScrActivity.class));
    }
    super.onCreate(savedInstanceState);
}

所有这些都是为了不覆盖片段的默认空构造函数,我知道不建议这样做.

现在,我需要获取我在此行中放入当前可见片段中的odTab对象的实例:

this.odTab = application.currentReport.getODTabByTabId(tabId);

有人可以向我解释如何做到这一点? 如何获得当前可见的片段实例,以便我可以将ODTAB对象从中删除?

更新: 通过我在这里收到的建议,我在称为currentVisibleTab的应用程序类中添加了一个odTab对象实例,我正在设置ODTAB的实例:

@Override
public void setUserVisibleHint(boolean isVisibleToUser) 
{
    Log.d(TAG, "setUserVisibleHint invoked!");
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) 
    {
        if (odTab != null && getActivity() != null)
        {
            Log.d(TAG, "Currently visable tab: "+odTab.getTabTitle());
            application.currentVisibleTab = odTab;
        }
    }   
}

更新2: 这是我的ViewPagerAdapter:

public class ViewPagerAdapter extends FragmentPagerAdapter 
{
private List<Fragment> fragments;

/**
 * @param fm
 * @param fragments
 */
public ViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
    super(fm);
    this.fragments = fragments;
}

/* (non-Javadoc)
 * @see android.support.v4.app.FragmentPagerAdapter#getItem(int)
 */

@Override
public Fragment getItem(int position) {
    return this.fragments.get(position);
}

/* (non-Javadoc)
 * @see android.support.v4.view.PagerAdapter#getCount()
 */

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

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
} 

public void removeAllFragments()
{
    this.fragments.clear();
}

public void addFragmentsListToAdapter(List<Fragment> fragments)
{
    this.fragments.addAll(fragments);
}

public List<Fragment> getFragments()
{
    return fragments;
}
}

中,我有ViewPager显示的片段List.此列表是这样初始化的:

 List<Fragment> fragments = new Vector<Fragment>();

我不明白的是如何获得从此列表触发接口方法的当前片段的引用. 这不是一个相关的问题,但也许您知道答案:List和Vector?

之间有什么区别

我仍然检查此选项.

推荐答案

我发现当前可见的片段的方式是setUserVisibleHint和一个接口回到ViewPager.此方法在 fragment 课.覆盖它,并使用接口回电给您的ViewPager.如果可见片段 - 即它返回true-,则只需使用对片段的引用(从列表备份适配器或存储为实例变量的列表中)就可以从片段本身中获取所需的内容.我添加了下面的代码.

声明与您想做的任何事情有关的接口.就我而言,当可见Google Map实例时,我正在使用它来禁用ViewPager默认x方向侦听器,以使地图的滚动不会触发片段的更改.

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

在片段中:

@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);
    }
}

直到我尝试出来的一件事是添加了ISATTACHED变量.我还覆盖片段onattach方法,并在称为片段后将其设置为True.否则会发生的是,用户将看到您的片段,并在初始化接口之前将接口调用到ViewPager.

在我的ViewPager中,我只是实现了onmapfragmentVisiblestener,然后添加所需的方法

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

在我的情况下,我使用它关闭ViewPager滑动功能.但是,我很容易回到我的地图中的公共方法.我恰好在ViewPager中有3个片段.

更新

要查找当前显示的片段,您首先需要找出正在显示的片段.您可以多种方法.最简单的是将一些描述符传递给您的ViewPager/FragmentActivity,然后使用您之前创建的界面.

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

设置的问题是您使用的是Vector,它确实是同步List来存储片段引用.结果,没有钥匙值可以在以后找到片段.片段具有通过添加到片段活性时在片段交易中创建的标签来识别的能力.但是,在A ViewPager设置中,您不会单独添加片段,因此在交易过程中无法添加标签.

我的解决方案是用BasePagerAdapter支持我的BasePagerAdapter,其中我存储了由唯一键标识的每个片段.

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

然后我在适配器中使用它:

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

由于您的引用存储在钥匙值中,因此很容易找到您要寻找的片段,因为您可以从HashMap>中搜索并返回.

如果您无法更改设置以远离Vector您需要(1)跟踪您添加哪个顺序(假设未添加和动态删除片段)以便您获得它的片段通过索引或(2)对每个片段保留单独的引用.

private Fragment x;

//in onCreate
x = new Fragment();

//in interface
if(tag.equals("x"){
    x.doSomething();
}

另一件事使这更容易,是我的片段都是子类别的.例如.执行不同函数的每个片段都有自己的类.由于每个类只有一个实例,因此很容易找到,因为我没有任何重复.如果您使用Fragment类的所有通用成员,则只需实现一些标记系统,这些系统使上述方法之一成为可能.

其他推荐答案

您可以使用以下方式使用viewpager.getCurrentItem()方法获得当前可见片段:

Fragment fr = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + mViewPager.getCurrentItem());

其他推荐答案

有点晚了,但是对于那些正在寻找更简单的东西,现在在2021年.

由于您的视图Pager需要一个适配器,并且您的适配器需要一个片段管理器,只需使用您的fragmentManager(Kotlin)

supportFragmentManager.fragments.first { it.isVisible && it.isAdded }

只是一个提醒,以这种方式使用binding.viewPager.offscreenPageLimit不能为> 0

或者您可以从viewpager这样使用currentItem;

supportFragmentManager.fragments[viewPager.currentItem]

本文地址:https://www.itbaoku.cn/post/102788.html

问题描述

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

 public void intialiseViewPager() 
 {
    List<Fragment> fragments = new Vector<Fragment>();
    numberOfTabs = application.currentReport.getODTabsList().size();
    for (int i = 0; i < numberOfTabs; i++)      
    {
        ODTab tempTab = application.currentReport.getODTabsList().get(i);
        if (tempTab.getTabType().equals(ODGrid.XML_GRID_ELEMENT))
        {
            GridFragment gridFragment = GridFragment.newInstance(tempTab.getTabId());
            fragments.add(gridFragment);
        }
        else  if (tempTab.getTabType().equals(ODChart.XML_CHART_ELEMENT))
        {
            NewChartFragment chartFragment = NewChartFragment.newInstance(tempTab.getTabId());
            fragments.add(chartFragment);
        }
    }
    Log.d(TAG, "Current report fragments set to adapter: "+fragments.toString());
    mPagerAdapter  = new ViewPagerAdapter(getSupportFragmentManager(), fragments);
    mViewPager = (ViewPager)findViewById(R.id.pager);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setOffscreenPageLimit(0);
    mViewPager.setOnPageChangeListener(this);
}

As you can see I pass to the fragment a String (tempTab.getTabId()), in this line:

GridFragment gridFragment = GridFragment.newInstance(tempTab.getTabId());

In the fragment itself I do this to initialize it:

public static final GridFragment newInstance(String tabId)
{
    GridFragment f = new GridFragment();
    Bundle bdl = new Bundle(2);
    bdl.putString(TAB_ID, tabId);
    f.setArguments(bdl);
    return f;
}

@Override
public void onCreate(Bundle savedInstanceState) 
{
    String tabId = getArguments().getString(TAB_ID);
    if (application.currentReport != null)
    {
        this.odTab = application.currentReport.getODTabByTabId(tabId);
    }
    else
    {
        startActivity(new Intent(getActivity(), LoginScrActivity.class));
    }
    super.onCreate(savedInstanceState);
}

All this is performed in order to not override the default empty constructor of the fragment, as I understand that this is not recommended.

Now I need to get the instance of the odTab object that I put into the currently visible fragment in this line:

this.odTab = application.currentReport.getODTabByTabId(tabId);

Could someone please explain to me how this could be done? How do I get the currently visible fragment instance so I can pull the odTab object out of it?

UPDATE: With the suggestions I received here I have added an odTab object instance to the application class that is called currentVisibleTab and I'm setting the instance of the odTab like this:

@Override
public void setUserVisibleHint(boolean isVisibleToUser) 
{
    Log.d(TAG, "setUserVisibleHint invoked!");
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) 
    {
        if (odTab != null && getActivity() != null)
        {
            Log.d(TAG, "Currently visable tab: "+odTab.getTabTitle());
            application.currentVisibleTab = odTab;
        }
    }   
}

UPDATE2: This is my ViewPagerAdapter:

public class ViewPagerAdapter extends FragmentPagerAdapter 
{
private List<Fragment> fragments;

/**
 * @param fm
 * @param fragments
 */
public ViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
    super(fm);
    this.fragments = fragments;
}

/* (non-Javadoc)
 * @see android.support.v4.app.FragmentPagerAdapter#getItem(int)
 */

@Override
public Fragment getItem(int position) {
    return this.fragments.get(position);
}

/* (non-Javadoc)
 * @see android.support.v4.view.PagerAdapter#getCount()
 */

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

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
} 

public void removeAllFragments()
{
    this.fragments.clear();
}

public void addFragmentsListToAdapter(List<Fragment> fragments)
{
    this.fragments.addAll(fragments);
}

public List<Fragment> getFragments()
{
    return fragments;
}
}

In it I have a List of fragments that are shown by the ViewPager. This list is initialized like this:

 List<Fragment> fragments = new Vector<Fragment>();

What I don't understand is how I get a reference to the current fragment that triggered the interface method from this list. And this is not a related question but maybe you know the answer: What is the difference between List and Vector?

I still check this option out.

推荐答案

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.

其他推荐答案

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());

其他推荐答案

A little bit late, but for those are looking for something more simple, now in 2021.

Since your view pager needs an adapter and your adapter needs a Fragment Manager just use you FragmentManager like this (Kotlin)

supportFragmentManager.fragments.first { it.isVisible && it.isAdded }

Just a reminder, to use this way your binding.viewPager.offscreenPageLimit cannot be > 0

Or you can use the currentItem from ViewPager like this;

supportFragmentManager.fragments[viewPager.currentItem]