【问题标题】:How to get the instance of the currently visible fragment in ViewPager:如何在 ViewPager 中获取当前可见片段的实例:
【发布时间】:2013-06-22 08:13:18
【问题描述】:

我正在构建一个在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>();

我不明白的是我如何从这个列表中获得对触发接口方法的当前片段的引用。 这不是一个相关的问题,但也许你知道答案:ListVector 有什么区别?

我仍然检查这个选项。

【问题讨论】:

  • 嘿@Raghunandan,我尝试了“setUserVisibleHint”选项,但由于某种原因没有成功。所以我发现这不是办法。我会再看看这个。
  • 覆盖setUserVisibleHint 并在setUserVisibleHint 中使用GridFragment.newInstance(tabId)。记录实例并检查。没有测试只是一个建议。
  • 我不太明白你的意思:“记录实例并检查”,请参阅更新的答案。我做的方向对吗?
  • 看起来不错。你想要 GridFragment 的实例对吗?

标签: android android-fragments android-viewpager instance


【解决方案1】:

我找到当前可见片段的方法是使用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 中,我只是实现了 OnMapFragmentVisibleListener 然后添加所需的方法

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

就我而言,我用它来关闭ViewPager 滑动功能。但是,我可以轻松地回调到 mapFragment 中的公共方法。我的 ViewPager 中碰巧有 3 个片段,我保留了参考。

更新

要找到当前显示的片段,首先需要找出正在显示的片段。您可以通过多种方式做到这一点。最简单的方法是使用您之前创建的接口将一些描述符传递回您的ViewPager / FragmentActivity

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

您的设置问题在于您使用的是Vector,它实际上是一个同步的List 来存储您的片段引用。因此,没有键值可用于稍后定位片段。当片段被添加到片段活动时,片段能够通过在片段事务期间创建的标签来识别。但是,在ViewPager 设置中,您不会单独添加片段,因此无法在事务期间添加标签。

我的解决方案是用HashMap 支持我的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 类的所有通用成员,您只需要实现一些标记系统,使上述方法之一成为可能。

【讨论】:

  • 能否提供一些代码 sn-p,说明您在片段端和 viewPager 端类上是如何做到的?
  • 刚刚添加 - 抱歉,在我回答时我不在办公桌前之前没有发帖。
  • 谢谢,伙计……非常详细的答案,如果这能解决我的问题,我会测试并接受你的答案,同时为你的努力+1。
  • 接口应该在哪里声明?
  • 在片段本身或在它自己的文件中。然后,您使用片段活动的类声明中的 implements 关键字来实现它,以便公共类 MyViewPager 扩展 FragmentActivity 实现 OnFragmentVisibleListener 抱歉,如果不清楚。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

    由于您的视图寻呼机需要一个适配器,而您的适配器需要一个 Fragment Manager,因此只需像这样使用 FragmentManager (Kotlin)

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

    提醒一下,使用这种方式你的binding.viewPager.offscreenPageLimit 不能> 0

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

    supportFragmentManager.fragments[viewPager.currentItem]
    

    【讨论】:

    • 如果您的活动中有多个片段而不是 viewpager 片段,这将不起作用。
    【解决方案4】:

    截至 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
            }
    
        }
    

    之后,我在viewpager之外的按钮点击器中使用了可见片段,如下:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-07
      • 2022-08-04
      • 2018-07-31
      • 2014-08-17
      • 2022-01-18
      • 1970-01-01
      相关资源
      最近更新 更多