FragmentPagerAdapter与ViewPager和两个Fragment一起使用。从第二个Fragment转到第一个Fragment并更新第一个Fragment的文本。

8

我对FragmentPagerAdapter不熟悉,所以这将是那些需要(你)批判性阅读说明的问题之一。

结构:我有一个FragmentPagerAdapter(下面是代码),它将同时持有两个片段。第一个显示书摘,第二个显示书名列表。

目标:我想实现标题中描述的功能:用户可以导航到页面器中的第二个片段,点击标题,然后将用户移回到第一个片段并告诉第一个片段更新文本。第一个片段有一个triggerRefresh方法来实现此目的。

代码:我认为我的问题发生在FragmentPagerAdapter重用/创建片段的方式上(我不理解这种方式)。这是我的类:

static class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {
        switch(position) {
        case 0:
            return new ExcerptsFragment();
        case 1:
            return new BookListFragment();
        default:
            throw new IllegalArgumentException("not this many fragments: " + position);
        }
    }
}

这是我创建相关成员的方法:
ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
MyFragmentPagerAdapter mFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mFragmentPagerAdapter);

当我从书标题片段收到所选标题的回调时,这是我在我的活动中尝试过的另一种方法:

mViewPager.setCurrentItem(0); // back to excerpts screen page. It's OK.
// Here's the problem! How to identify the fragment 0 
// to ExcerptsFragment and call its triggerRefresh()?!?

问题系列:

调用适配器的 getView() 方法不起作用,因为它将返回一个新的 ExcerptsFragment 实例,而这个实例并没有被当前附加(如预期的那样,会抛出异常)。

我在这里看到很多人(示例)只是在 getView() 中存储片段。这样做对吗?因为从官方示例来看,似乎是一种反模式(通过持有项目来打败自动引用)。这也是这里这里的观点(并且在我看来是正确的)。

有什么建议吗?如果我一点都不理解这个问题,我也不会感到惊讶...

3个回答

7

免责声明:尽管这在之前对我来说完美地运行,您应该意识到依赖内部私有行为的经典陷阱。虽然我编写了测试以警告我如果内部实现发生变化,但我已经转向更好的选择。您也应该如此。因此,在我看来,这个问题及其答案的价值仅仅是历史性的。


对于那个问题感到抱歉,我认为那是时间的问题。

为了解决这个问题,我按原样实现了this solution。看起来很好用。所以,我相信只是找到了(当前附加的)片段实例,通过找出它的Id是如何命名的。上面的链接解释了它是如何制作的。

我选择回答自己的问题,而不是删除它,因为我相信像我这样的新手在这些页面上将受益于“真实案例”。我看到的大多数答案都谈论理论,这是正确的方式,但有时没有一个真实的例子可以工作,像我这样的人就会迷失方向。

无论如何,这是我需要的最后一段代码(上面的注释部分):

int n = 0;
mViewPager.setCurrentItem(n); // in the question I had stopped here.

ExcerptsFragment f = (ExcerptsFragment) ContainerActivity.this
        .getSupportFragmentManager().findFragmentByTag(getFragmentTag(n));
f.triggerRefresh();

// ... below the helper method: used the solution from the link.

private String getFragmentTag(int pos){
    return "android:switcher:"+R.id.pager+":"+pos;
}

因为我没有持有任何片段的引用(从而 risking 引用过时),所以我有一种这是一个强大的解决方案的感觉。我将自己的代码保持在最低限度,从而最小化了我做一些愚蠢事情的机会。

当然,如果您有什么要补充的,要展示给我们看的,要告诉我们做错了什么或可以改进什么,我很乐意听取您的意见。


不应依赖于分配标签的内部机制的兼容性。请参考我对类似问题的回答,了解如何正确处理此问题:https://dev59.com/G2Yr5IYBdhLWcg3wAFg0#41345283 - morgwai
@morgwai 谢谢你的警告。我已经加入了免责声明来警告潜在读者。 - davidcesarino
不用谢 :) 我认为直接提供链接到我的答案会更好(不仅仅是那个问题),因为我的答案非常新,因此得分还很低,读者可能会卡在两个得分最高的答案之一上,这两个答案都有问题。(我在我的答案中添加了一些解释,说明为什么仅覆盖instantiateItem的解决方案可能不足够) - morgwai
抱歉,我得放弃了。我从未链接或以任何方式直接或间接地认可我仍未使用、测试或确认的解决方案(正如我在答案中所说,我已经摆脱了“解决方案”(引号)相当长的时间)。作为个人评论,我建议您不要担心赞和可见性。如果有一件事我在这些年的SO上注意到的是,如果用户遇到问题并且解决方案更好,能够解决他们的问题,他们会找到最佳答案并给予赞,不要担心这个。如果其他答案存在问题,让用户自己判断。;-) - davidcesarino
我并不是非常在意我的UIP(无用的互联网积分),但我关心的是人们被错误答案的高分误导,但随便了;) - morgwai

2
我自己也曾经搜索过这个问题的解决方法。你的方法原则上是可行的,但如果Android基类实现中片段标记创建的代码发生变化,它将破坏你的代码。这是一个非常讨厌的依赖关系!
更优雅的方法是反过来考虑,并在片段中保留基本活动的实例。在您的活动中实现一个设置器来设置标记,然后在片段创建时调用该设置器——那里的标记只需使用getTag()即可获取。
可以在这里找到一个示例实现。

到目前为止,这是解决这个非常随机的问题的最佳方案。但是需要注意两件事情:您可以使用getActivity()获取对“父活动”的引用。并且您需要记住在配置更改期间保留标签名称,因为即使适配器可能会重新创建,片段可能不会(因此标签将不会再次设置)。 - jpm

0

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