ViewPager在滑动时更新Fragment

29

我有一个问题已经困扰我两天了。

我正在构建一个应用程序,使用ActionBar、ViewPager和FragmentPagerAdapter。Activity、Fragments和FragmentPagerAdapter的代码与Android示例中http://developer.android.com/reference/android/support/v4/view/ViewPager.html上的代码完全相同。

我面临的问题是——假设在viewPager中只有两个片段。当在两个片段之间切换/滑动时,片段不会更新。onResume不会被调用,因为viewPager会缓存当前片段左右各1个片段。

我尝试使用onTabSelected来检测选择的片段,然后通过接口从该片段启动一个方法(以下是代码)。

public void onTabSelected(Tab tab, FragmentTransaction ft) {
    TabInfo tag = (TabInfo)tab.getTag();
    for (int i=0; i<mTabs.size(); i++) {
        if (mTabs.get(i) == tag) {
            mViewPager.setCurrentItem(i);
        }
    }
    ((IStartStop)getItem(tab.getPosition())).Start();
}

然而,当使用Start方法时,在尝试更新TextView时会引发NullPointerException。 start方法的代码如下:

public void Start() {
    TextView tv = _view.findViewById(R.id.text);
    tv.setText("test");
}

异常抛出位置在:

TextView tv = _view.findViewById(R.id.text);

IStartStop 接口非常简单:

public interface IStartStop {
    public void Start();
    public void Stop();
}

我不想使用notifyDataSetChanged()和POSITION_NONE,因为每次我滑动到一个新的片段时,加载片段需要几秒钟的时间。

此时,片段只包括一个文本视图,在未来它们将具有动画,因此很重要:

1- 仅在选择片段时运行动画,而不是在选择旁边的片段时运行动画(ViewPager缓存并恢复片段的方式)。

2- 当片段不再被选中时停止动画,以避免浪费设备资源。

是的,我已经检查了互联网上所有可用的信息,但似乎都不能解决我的问题。

非常感谢您的帮助!


https://androidbeasts.wordpress.com/2015/08/11/tabs-with-swipe-views/#more-79 - Aakash
4个回答

60

惊奇的是,ViewPager在这方面并不支持“原生”(还有其他一些事情)。但并非一切都失去了希望。

首先,您需要修改片段,以便它们仅在您告诉它们可以运行动画时运行动画,而不是在实例化时运行。这样,您可以玩弄viewpager偏移量(默认值= 3),预加载2-3个片段,但不进行动画处理。

第二步是创建一个接口或类似于定义“片段何时变为可见”的内容。

第三步是将新的OnPageScrollListener附加到您的viewpager上。

以下是代码(半未测试代码):

1)附加侦听器:

mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(final int i, final float v, final int i2) {
            }
            @Override
            public void onPageSelected(final int i) {
                YourFragmentInterface fragment = (YourFragmentInterface) mPagerAdapter.instantiateItem(mViewPager, i);
                if (fragment != null) {
                    fragment.fragmentBecameVisible();
                } 
            }
            @Override
            public void onPageScrollStateChanged(final int i) {
            }
        });

2)这是您的界面:

public interface YourFragmentInterface {
    void fragmentBecameVisible();
}

3) 更改你的片段,使它们实现这个功能:

public class YourLovelyFragment extends Fragment implements YourFragmentInterface {

4) 在片段中实现接口

@Override
public void fragmentBecameVisible() {
    // You can do your animation here because we are visible! (make sure onViewCreated has been called too and the Layout has been laid. Source for another question but you get the idea.
}

接下来该怎么做?

您可能希望实现一种方法/监听器,以通知“其他”片段它们不再可见(即当一个可见时,其他片段不可见)。但这可能并不是必要的。


嗨,马丁,你的评论确实有道理。但我需要回家后再试一下。然而,如果我理解你的意思正确的话,你的解决方案建议使用setOffscreenPageLimit(3)来维护片段,不应该在onResume()中放置任何代码,并且手动调用动画。这与我在我的代码中所做的非常相似: ((IStartStop)getItem(tab.getPosition())).Start();但是不在onResume()中放置任何东西的技巧可能真的有效。我会尝试并让你知道结果。谢谢! - Steve
1
再次你好,@Martín,这是我发现的问题,NPE被抛出是因为我在代码中调用了Fragment.instantiate(mContext, info.clss.getName(), info.args),然而你的方法PagerAdapter.instantiateItem(vViewPager, position);却有效。另外,如同我的代码,所有从onResumeonPause中移除的方法都会从onPageChanged事件中调用。我简直不敢相信我甚至没有怀疑getItem()引起了所有这些问题,但我想这就是当你每天工作9个小时并且花费超过6个小时的夜晚来完成其他工作时所发生的事情。谢谢! - Steve
1
嗯,这是一个已知的问题,但问题要求在两个片段之间切换或滑动时,片段没有得到更新,因为想要在其发生改变时得到通知,而不是在第一次加载时。不过你可以很容易地解决这个问题... 这里在StackOverflow上有其他一些答案哦。;) - Martin Marconcini
1
谢谢Martin!我已经花了一整天的时间寻找这个问题的解决方案,你的解决方案完美地解决了它! - marty331
@Lila 很高兴它对你有用!这曾经是一种常见的Android模式(我已经六个月没碰过Android了,不确定现在是否有更新的东西,我怀疑)。;) 祝你好运! - Martin Marconcini
显示剩余4条评论

4

这将影响在视图页面中从一个页面到另一个页面的更改。

OnPageChangeListener  pagechangelistener =new OnPageChangeListener() {

                @Override
                public void onPageSelected(int arg0) {
                    Logger.logMessage("Called first");

                    pageAdapter.notifyDataSetChanged();
                    indicator.setCurrentItem(arg0);
                }

                @Override
                public void onPageScrolled(int arg0, float arg1, int arg2) {

                    Logger.logMessage("Called second");

                }

                @Override
                public void onPageScrollStateChanged(int arg0) {

                    Logger.logMessage("Called third");

                }
            };
            myViewPager.setOnPageChangeListener(pagechangelistener);

在您的页面适配器中使用此代码。

@Override
    public int getItemPosition(Object object) {

        return POSITION_NONE;
    }

2

setOnPageChangeListener已经被弃用。相反,您应该使用addOnPageChangeListener。

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(final int i, final float v, final int i2) {
        }
        @Override
        public void onPageSelected(final int i) {
            YourFragmentInterface fragment = (YourFragmentInterface) mPagerAdapter.instantiateItem(mViewPager, i);
            if (fragment != null) {
                fragment.fragmentBecameVisible();
            } 
        }
        @Override
        public void onPageScrollStateChanged(final int i) {
        }
    });

-7

在定义 Viewpager 时添加此代码

mViewPager.setOffscreenPageLimit(0);

当您切换到其他片段时,它不会保存任何片段的视图..因此从一个片段切换到另一个片段将从头开始创建该片段并更新所有内容。


请不要这样做... 这几乎破坏了FragmentPageAdapter内部的优化。 - Martin Marconcini
1
嗨, 即使我想使用它,mViewPager.setOffscreenPageLimit(0) 的默认值为1,如果使用0会抛出异常。 - Steve

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