ViewPager 片段随着时间的推移而被破坏?

Posted

技术标签:

【中文标题】ViewPager 片段随着时间的推移而被破坏?【英文标题】:ViewPager Fragments getting destroyed over time? 【发布时间】:2012-04-09 02:07:30 【问题描述】:

我正在使用androidsupport.v4包来开发一个包含多个Fragments的ViewPager。

我试图在用户更改页面时隐藏视图。因此,我实现了一个OnPageChangeListener 来监听我的适配器,并在发生页面更改时在我的Fragments 中调用update() 方法。但是我收到了NullPointerException,我不知道为什么。

代码如下:

public class MyActivity extends FragmentActivity implements
        OnPageChangeListener 

    private static final int NUM_ITEMS = 2;
    private ViewPager mPager;
    private static SpaceAdapter mAdapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_pager);

        mAdapter = new SpaceAdapter(getSupportFragmentManager());
        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

        mPager.setOnPageChangeListener(this);


    public static class SpaceAdapter extends FragmentPagerAdapter 

        public SpaceAdapter(FragmentManager fm) 
            super(fm);
        

        @Override
        public int getCount() 
            return NUM_ITEMS;
        

        @Override
        public Fragment getItem(int position) 
            switch (position) 
            case 0:
            return new MyFragment();
            case 1:
            return new MyFragment();
            default:
            return new MyFragment();
            
        
    

    @Override
    public void onPageScrollStateChanged(int arg0) 
        Log.i("pagination", "scroll state changed: " + arg0);


    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) 
        // Log.i("pagination", "page scroll: " + arg0 + " with: " + arg1 + ", " + arg2);
        if (mAdapter.getItem(arg0) instanceof IUpdateOnPage) 
            ((IUpdateOnPage) mAdapter.getItem(arg0)).updateOnPage();
        
    

    @Override
    public void onPageSelected(int arg0) 
        Log.i("pagination", "page selected: " + arg0);
    

public class MyFragment extends Fragment implements
        SurfaceHolder.Callback, IUpdateOnPage 

    private SurfaceView charts;

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
    

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) 
        View v = inflater.inflate(R.layout.crew, null);
        bindViews(v);
        return v;
    

    private void bindViews(View v) 
        charts = (SurfaceView) v.findViewById(R.id.civ_charts);

        charts.getHolder().addCallback(this);
        Log.i("wtf", "huh " + String.valueOf(charts == null)); // logs false
    

    @Override
    public void updateOnPage() 
        if (charts != null) 
            Log.i("crew surface", "hidden");
            charts.setVisibility(View.GONE);
         else 
            Log.i("crew surface", "charts is null");  //THIS HERE gets logged
        
    

w当我复制/粘贴时,我删除了 SurfaceHolder.Callback 方法,但它们在那里。 bindViews(v) 方法记录 charts == null,并且总是返回 false。当我页面时,updateOnPage 被调用,charts == null 返回 true。为什么会发生这种情况,我该如何解决?

这是第 108 行:

charts.setVisibility(View.GONE);

【问题讨论】:

好的,经过进一步调查,我假设片段在分页开始时被破坏(即使视图仍然存在?)。滑动时,我仍然可以看到“一半”片段,直到新片段捕捉到全视图。所以,如果所有的引用都被收集了,剩下的就是视图,我该怎么做呢? 如果您收到NullPointerException,您可以发布 logcat 输出吗? 好吧,我实际上正在捕捉它,因此不会引发异常。问题是charts.setVisibility(View.GONE);。图表为空。它位于第二个代码示例的最后,位于else 的正上方。添加了堆栈跟踪。 【参考方案1】:

初始化ViewPager时添加以下内容,

mPager.setOffscreenPageLimit(NUM_ITEMS-1);

在您的情况下,这会将屏幕外页面的数量设置为 1(即,当另一个页面当前处于焦点时,它将在内存中保留额外的屏幕外页面)。这将强制您的 ViewPager 保留所有 Fragments,即使它们不在焦点上。

【讨论】:

解决了我遇到的另一个小性能问题,非常感谢!但是当这个片段使用相同的空指针显示时它仍然会崩溃.. 实际上,这解决了核心问题。我不再需要调用 setVisibilty(View.gone) 了。奇怪,但谢谢!澄清一下——SurfaceView 没有正确分页,即使我滑动它,它仍然保留在所有页面上。 这很奇怪,因为 setOffscreenPageLimit 在 v4 中默认为 1(并且 NUM_ITEMS - 1 等于 1),但我很高兴您的问题已得到解决。 实际上我将代码“笨拙”了一点,然后将其粘贴到此处。我有 7 个片段,但这是唯一一个有表面视图的片段,也是唯一一个有问题的片段......不过谢谢! 很高兴听到您成功了。在我看来,ViewPager 的生命周期没有得到很好的记录......这可能有点棘手。【参考方案2】:

我检查了您的代码,我认为问题在于在onPageScrolled 中,您正在调用getItem。这个实现(实际上就像它在 web 上的大多数实现一样)每次都返回一个新实例。但是,我见过的大多数示例都不会调用getItem,所以这通常没问题。

在您的情况下,我会在 getItem 中懒惰地创建片段:如果该位置不存在,则创建它,然后将其挂在 ArrayList 或其他集合中,但如果它已经存在,只需从该集合中返回它。

否则,你创建一个片段实例 A,然后滚动到另一个片段实例 B。你试图告诉片段实例 A 清理一些视图,但你最终告诉一个新的片段实例 C 这样做。它还没有机会建立自己的视图,所以你得到了指向图表的空指针。

【讨论】:

哦,这很好。我从某处的谷歌示例中获得了这个实现,但我将研究实现一个数组或其他东西来维护片段。谢谢!

以上是关于ViewPager 片段随着时间的推移而被破坏?的主要内容,如果未能解决你的问题,请参考以下文章

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

标签颜色之间的交叉渐变随着时间的推移在tkinter中?

如何判断 Vue 组件是不是因为热重载而被破坏?

Android:将“ViewPager”动画从片段更改为片段

从 Viewpager2 片段访问父片段函数

从 ViewPager 中检索片段