如何在ViewPager中获取当前可见fragment的实例:

3
我正在开发一个应用程序,该应用程序在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;
}
}

我有一个包含List的片段,这些片段由ViewPager显示。该列表是这样初始化的:

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

我不明白的是如何从列表中获取触发接口方法的当前片段的引用。这不是一个相关的问题,但也许你知道答案:List和Vector之间有什么区别?
我仍在检查这个选项。

请查看此链接:https://dev59.com/iWkw5IYBdhLWcg3wQoSm。 - Raghunandan
嘿@Raghunandan,我尝试了“setUserVisibleHint”选项,但由于某种原因没有成功。所以我想出这不是正确的方法。我会再次查看这个问题。 - Emil Adz
重写 setUserVisibleHint 方法,并在其中使用 GridFragment.newInstance(tabId)。记录实例并进行检查。这只是一个建议,未经测试。 - Raghunandan
我不是很明白你所说的“记录实例并检查”的意思,请参考更新后的答案。我现在所做的是否正确? - Emil Adz
看起来不错。你想要 GridFragment 的实例,对吗? - Raghunandan
显示剩余3条评论
4个回答

6
我通过使用setUserVisibleHint方法和与ViewPager的接口,来找到当前可见的片段。此方法在片段类中。覆盖它并使用接口回调到您的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中,我只需实现OnMapFragmentVisibleListener,然后添加所需的方法即可。

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

在我的情况下,我使用它来关闭 ViewPager 的滑动功能。但是,我可以很容易地调用我的 mapFragment 中的公共方法。 我碰巧有 3 个片段在我的 ViewPager 中,我保留了对它们的引用。 更新 要找到当前显示的片段,首先需要找出正在显示哪个片段。你可以通过多种方式实现这一点。最简单的方法是通过之前创建的接口将某些描述符返回给你的 ViewPager / FragmentActivity
public interface OnFragmentVisibleListener{
    public void fragmentVisible(boolean true, String tag);
}

你的设置问题在于你使用了一个同步的List,也就是Vector来存储你的片段引用。结果,没有任何键值可用于以后定位你的片段。片段有一个标签,可以在添加到片段活动时创建,从而识别它们。然而,在ViewPager设置中,你不能单独添加片段,因此无法在事务期间添加标签。
我的解决方案是在我的BasePagerAdapter中使用一个HashMap来支持每个片段,并通过唯一的键来识别它们。
 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类的所有通用成员,则只需要实现一些标记系统,使上述方法之一成为可能。


在片段本身或其自己的文件中。然后,在您的类声明中使用implements关键字为片段活动实现它,因此public class MyViewPager extends FragmentActivity implements OnFragmentVisibleListener。如果不清楚,请见谅。 - Rarw
好的,我已经做完了,我在我的GridFragment中创建了接口,它被称为"OnFragmetVisibleListener",并让我的ViewPager活动来实现此接口,添加未实现的方法,所以这基本上让我知道片段当前是可见的,但我不明白如何现在获取可见的片段实例? - Emil Adz
当你知道片段是可见的后,你需要访问存储在FragmentActivity中的对该片段的引用。你可以通过多种方式实现这一点。当我只需要跟踪少量片段时,我将引用作为实例变量存储。如果有更多的话,只需查看适配器即可。你必须在ViewPager中有一个包含片段的集合。一旦你收到回调说片段x可见了,就从集合中获取该引用即可。 - Rarw
刚看到这个更新 - 你接受了,这是否意味着你已经解决了问题?(顺便说一下,我不知道向量和列表之间的区别) - Rarw
我的片段都是从服务器下载的数据填充的。你也可以做到!祝你好运。 - Rarw
显示剩余8条评论

4
您可以使用以下方法通过 viewpager.getCurrentItem() 方法获取当前可见的 Fragment:

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

2

有点晚了,但对于那些寻找更简单方法的人,现在是2021年。

由于您的视图页面需要一个适配器,而您的适配器需要一个片段管理器,因此只需像这样使用您的FragmentManager(Kotlin)即可:

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

提醒一下,如果要使用这种方式,你的binding.viewPager.offscreenPageLimit不能大于0。

或者你可以像这样使用ViewPager的currentItem

supportFragmentManager.fragments[viewPager.currentItem]

如果您的活动中除了ViewPager片段之外还有多个片段,则此方法将无法正常工作。 - AndroidDev

0

截至2020年,我在Kotlin中的做法如下:

在片段类中,我添加了一个公共变量:

var fragmentNo = 0

fragmentNo 变量是通过适配器的 getItem 填充的,具体如下(请注意 fragment.fragmentNo = pos):

    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
        }

    }

接下来,我将在视图页之外的按钮点击器中使用可见片段,如下所示:

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

        }

以下代码行会找到特定的片段:
val fr: ExerciseFragment? = supportFragmentManager.fragments.find {
                it is ExerciseFragment && it.fragmentNo == viewPager.currentItem
            } as? ExerciseFragment

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接