PagerAdapter两次实例化类

时间:2016-09-14 23:58:02

标签: java c# android android-fragments xamarin.android

我已就此问题做了大量阅读,但我仍然对如何解决这个问题感到茫然。基本上,我有一个视图寻呼机,然后有一个寻呼机适配器类,并将使用

实例化我的片段
    public override Android.Support.V4.App.Fragment GetItem(int position)
    {

        switch (position)
        {
            case 0:
                return new Batches();
            case 1:
                return new Track();
            case 2:
                return new Post();
            case 3:
                return new Upload();
            case 4:
                return new Admin();
            default:
                return new Track();
        }


    }

据我所知,这个命名有误导性(基于我读过的另一篇SO帖子),因为这个方法实际上是为类创建实例,或者如果已经创建的话,则返回它(即当你刷页面时。

也就是说,当您刷卡时,我需要一些自定义生命周期事件来提升我的碎片。根据我在SO上做的其他阅读,确定创建自定义界面是最好的方法。所以,这就是我所做的:

接口

interface ICustomFragmentLifecycleForPager
{
    void onPausePagerFragment();
    void onResumePagerFragment(int prevPage);
}

基于视图寻呼机页面更改事件的活动调用

    private void ViewPager_PageSelected(object sender, ViewPager.PageSelectedEventArgs e)
    {
        int position = (int)e.Position;

        ICustomFragmentLifecycleForPager fragmentToResume = (ICustomFragmentLifecycleForPager)adapter.InstantiateItem(viewPager, position); 
        fragmentToResume.onResumePagerFragment(previousActiveFragment);

        ICustomFragmentLifecycleForPager fragmentToPause = (ICustomFragmentLifecycleForPager)adapter.InstantiateItem(viewPager, previousActiveFragment);
        fragmentToPause.onPausePagerFragment();

        previousActiveFragment = position;
    }

最后,一个片段

的实现示例
    public void onPausePagerFragment()
    {
        //deInitScanner();
    }

现在,这就是问题所在。虽然我的寻呼机适配器类上面的GetItem调用应该只创建第一个实例,否则返回实例,我发现我的接口将调用GetItem并且它会创建一个类的第二个(不需要的)实例。

当我的应用程序启动时,在onResume生命周期中,我有一个方法调用,它确定默认情况下在启动时显示哪个页面。然后该逻辑调用另一种方法来执行页面更改。当它执行页面更改时,将为我的自定义生命周期引发ViewPager_PageSelected事件。因为那些使用InstantiateItem(也许这就是问题),它将实例化该类的新实例。我确保在GetItem运行之后我不设置页面并为我的所有类创建实例,但是在稍后调用它时它仍会创建一个新的。

如果我在应用程序中遇到一个断点,我会看到这个,我在SupportFragmentManager中有一个实例,当我检查它时,我看到了我的片段的两个实例。

我怀疑问题是: 1.我需要在我的界面上使用与InstantiateItem不同的东西来强制它获取已经存在的类的实例吗? 2. GetItem调用看到InstantiateItem是从视图分页器以外的其他东西调用的,因此具有不同的标记,因此,GetItem认为它没有创建该类的实例并创建另一个实例?

无论哪种方式,我都不确定如何解决这个用例。

请帮忙!

编辑 - 我已经尝试过这个解决方案来存储类,如果它已经实例化并返回,而不是每次都创建新的。基本上在寻呼机适配器类中,全局到GetItem,我为每个类做了类似的事情:

Batches batches;
Track track;

然后,在GetItem方法中,我稍微修改了case语句:

        switch (position)
        {
            case 0:
                if (batches == null)
                {
                    batches = new Batches();
                }
                return batches;
            case 1:
                if (track == null)
                {
                    track = new Track();
                }
                return track;

我切断了它,但是我为所有的情况做了。但是,我现在得到了这个例外。

  

Java.Lang.IllegalStateException:已添加片段:跟踪{42350aa8#1 id = 0x7f070097 android:switcher:2131165335:1}

编辑 - 这是堆栈跟踪的屏幕截图。没有太多表现。 Stack Trace of Exception

谢谢!

2 个答案:

答案 0 :(得分:0)

  

此方法实际上为类创建实例,如果已创建

则返回它

如果你告诉它,这个方法只会满足这个要求。目前,每次调用此方法时,您都在创建所请求类的新实例。

要解决此问题,您需要在第一次创建实例时存储实例。然后,如果已经创建了相同的实例,则可以返回该实例。这样做需要更多代码。

答案 1 :(得分:0)

经过一些线索,更多的阅读和一些测试,我相信我找到了答案。但是,如果有人反对或知道更好的方法,那么请纠正我。

当与寻呼机适配器一起使用时,我了解到,如果仅使用基本实现,InstantiateItem将每次创建一个类/片段的实例。开发人员可以覆盖它并在创建后存储创建的片段,然后使用GetItem返回它。我也知道InstantiateItem会调用GetItem。

我相信发生的事情是,我的GetItem实现只是创建片段的一个实例。在创建它们之后,我没有明确地存储它们的实例。也就是说,这并不是完全必要的,因为如果SupportFragmentManager已经有了该类的实例,则不需要调用GetItem,这就是每次都不创建多个实例的方式。

也就是说,我实现的自定义生命周期接口(参见上面的OP)是调用InstantiateItem。因此,当调用和使用它时,它将创建我的片段的另一个实例,因为之前已创建的实例具有不同的标记名称。所以,我会有两个(但只有两个)。一个由寻呼机适配器创建的,一个是由我的界面调用的InstantiateItem创建的。

所以,修复是(似乎有效)是:

  1. 更改GetItem,以便在创建片段后存储对片段的引用,并返回存储的实例或如果为null则创建new。喜欢这个

     Batches batches;
    
     if (batches == null) {
         batches = new Batches();
     }
     return batches;
    
  2. 确保寻呼机适配器类已完成,并且GetItem已在接口可以调用之前为类创建了一个实例(并存储它们)。这可确保创建和存储的实例是寻呼机适配器的实例,并具有正确的标记。

  3. 最后,最大的变化。我正在使用的接口的实现是调用InstantiateItem(我从SO得到这个)来获取对类的引用。如上所述,这是创建一个新实例。但是,如果我将其更改为GetItem,它将简单地获取并返回由寻呼机适配器创建和存储的片段的实例。所以,它应该是这样的:

    ICustomFragmentLifecycleForPager fragmentToResume = (ICustomFragmentLifecycleForPager)adapter.GetItem(position);  //InstantiateItem(viewPager, position);
     fragmentToResume.onResumePagerFragment(previousActiveFragment);
    
  4. 希望这有帮助!