滑动时 ViewPager 更新片段

Posted

技术标签:

【中文标题】滑动时 ViewPager 更新片段【英文标题】:ViewPager update fragment on swipe 【发布时间】:2013-12-23 02:59:01 【问题描述】:

我有一个问题,过去 2 天我一直在努力解决。

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

我面临的问题是——假设我在 viewPager 中只有 2 个片段。在两者之间切换/滑动时,片段没有得到更新。 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 方法时,尝试更新文本视图时会触发 NullPointerException。启动方法的代码是:

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 因为每次我滑动到新片段时,加载片段需要几秒钟

此时,fragment 只包含一个 textview,以后会有动画,所以很重要:

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

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

是的,我已经检查了互联网上的所有可用内容,但似乎没有任何东西适合我。

非常感谢您的帮助!

【问题讨论】:

androidbeasts.wordpress.com/2015/08/11/tabs-with-swipe-views/… 【参考方案1】:

令人惊讶的是,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 中没有任何内容的技巧可能确实有效。我会试试看,让你知道谢谢! 嗨@Martín,这是我发现的,NPE 被抛出,因为我在Fragment.instantiate(mContext, info.clss.getName(), info.args) 上面的代码中调用,但是你的方法有效PagerAdapter.instantiateItem(vViewPager, position);。同样在我上面的代码中,onResumeonPause 形式的所有方法都被删除并从onPageChanged 事件中调用。我不敢相信我什至没有怀疑getItem() 会导致所有这些,但我想这就是当你每天工作 9 个小时并在晚上花费超过 6 个小时试图完成你的其他工作时发生的事情。谢谢! 感谢马丁·马康西尼。我花了一整天,但找不到更好的解决方案。 mPagerAdapter.instantiateItem(mViewPager, i);这行代码解决了我的问题。 嗯,这是一个已知的问题,但问题要求在两者之间切换/滑动时,片段没有得到更新,因为这个想法是在它更改时通知,而不是在第一次加载时通知。你可以很容易地解决这个问题…… *** 上还有一些其他的答案。 ;) 谢谢马丁!我花了一天时间寻找这个问题的解决方案,您的解决方案非常有效!【参考方案2】:

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

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;
    

【讨论】:

【参考方案3】:

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

【讨论】:

【参考方案4】:

在定义 Viewpager 时添加此代码

mViewPager.setOffscreenPageLimit(0);

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

【讨论】:

请不要那样做……这或多或少会破坏 FragmentPageAdapter 内部的优化。 嗨,如果我想使用它,mViewPager.setOffscreenPageLimit(0) 的默认值为 1,如果使用 0,则会抛出异常。

以上是关于滑动时 ViewPager 更新片段的主要内容,如果未能解决你的问题,请参考以下文章

滑动 ViewPager 时如何清除片段相关状态

使用 ViewPager 滑动时,片段没有调用 OnResume()

我的片段中有 webview,当滑动 webview 时, viewPager 也开始滑动

如何在 viewpager 中显示 2 个片段

在 ViewPager 中滑动方向

将 ViewPager 与 Fragments 一起使用时,在滑动时会破坏 Fragment