Android 第一次之后未显示Viewpager片段

Android 第一次之后未显示Viewpager片段,android,android-fragments,android-viewpager,Android,Android Fragments,Android Viewpager,我有一个包含3个片段(A、B、C)的活动。片段A由一个ViewPager和2个ListFragments组成。用户可以点击任何listfragments中的一个项目,然后点击进入片段B 在片段A中,我会: @Override public void onAttach(Activity activity) { super.onAttach(activity); pagerAdapter = new PagerAdapter(getActivity().getSupportFragm

我有一个包含3个片段(A、B、C)的活动。片段A由一个
ViewPager
和2个
ListFragments
组成。用户可以点击任何listfragments中的一个项目,然后点击进入片段B

在片段A中,我会:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager());
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragmentA, container, false);

    vpPager = (ViewPager)view.findViewById(R.id.vpPager);
    vpPager.setOffscreenPageLimit(2);
    vpPager.setAdapter(pagerAdapter);
    vpPager.addOnPageChangeListener(this);

    return view;
}
PagerAdapter
如下所示:

private class PagerAdapter extends FragmentPagerAdapter {
    private final ListFragment1 lf1 = ListFragment1 .newInstance();
    private final ListFragment2 lf2 = ListFragment2 .newInstance();

    public PagerAdapter(android.support.v4.app.FragmentManager fm) {
        super(fm);
    }

    @Override
    public android.support.v4.app.Fragment getItem(int position) {
        switch (position) {
            case 0:  return lf1;
            case 1:  return lf2;
            default: return null;
        }
    }
}
@Override
public void onPageSelected(int position) {
    Fragment fragment = pagerAdapter.getItem(position);
    if (fragment instanceof IPagedFragment) {
        ((IPagedFragment) fragment).onShown();
    }
}
第一次显示活动时,viewpager列表片段将正确显示

2个viewpager片段从数据库加载数据,我只做了一次(创建片段时)

用户可以点击一个项目并显示片段B。如果用户按下返回键,则显示片段A。但是,没有显示列表片段(它们的一个实例已经存在)

即使存在实例,视图是否已被破坏

这里怎么了?有更好的方法吗

编辑

如果我在寻呼机适配器中使用
newInstance
,我会得到一个
IllegalStateException:notattached to activity
。这是因为我启动了一个异步任务,如下所示:

private class PagerAdapter extends FragmentPagerAdapter {
    private final ListFragment1 lf1 = ListFragment1 .newInstance();
    private final ListFragment2 lf2 = ListFragment2 .newInstance();

    public PagerAdapter(android.support.v4.app.FragmentManager fm) {
        super(fm);
    }

    @Override
    public android.support.v4.app.Fragment getItem(int position) {
        switch (position) {
            case 0:  return lf1;
            case 1:  return lf2;
            default: return null;
        }
    }
}
@Override
public void onPageSelected(int position) {
    Fragment fragment = pagerAdapter.getItem(position);
    if (fragment instanceof IPagedFragment) {
        ((IPagedFragment) fragment).onShown();
    }
}
onShown
是:

@Override
public void onShown() {
    myTask= new MyTask();
    myTask.execute((Void)null);
}

何时可以启动任务,以便100%确定片段已附加到活动,并且视图已创建(我需要从布局中获取listview等)。

尝试覆盖FragmentPagerAdapter中的
getItemPosition
方法:

@Override
public int getItemPosition(Object object) {
    return PagerAdapter.POSITION_NONE;
}

您不应该在
FragmentPagerAdapter
中保留对片段的引用。您应该始终在
getItem()
call中调用
newInstance
,例如:

@Override
public android.support.v4.app.Fragment getItem(int position) {
    switch (position) {
        case 0:  return ListFragment1.newInstance();
        case 1:  return ListFragment2.newInstance();
        default: return null;
    }
}
从数据库加载的数据应该存储在片段本身中。适配器将恢复片段的状态(
setOffscreenPageLimit(2)

您正在丢失片段,因为项目(片段)由您提供的
FragmentManager
实例化,并且它根据标记创建片段。所以它可能会创建一个你已经保留的片段的新实例,只是使用不同的标记

请参阅
FragmentPagerAdapter
源代码(检查
InstanceItem()
方法):

另请参见以下答案:
您正在使用Activity FragmentManager创建ListFragment1和ListFragment2,而您应该使用FragmentManager。因此,修改
pagerAdapter=newPageRadapter(getActivity().getSupportFragmentManager())带有
pagerAdapter=新的pagerAdapter(getChildFragmentManager())。这样,视图寻呼机的片段将“绑定”到承载视图寻呼机的片段。此外,您不应该在viewpager中保留任何对片段的引用:这是安卓已经在管理的东西。尝试:

private class PagerAdapter extends FragmentPagerAdapter {

    public PagerAdapter(android.support.v4.app.FragmentManager fm) {
        super(fm);
    }

    @Override
    public android.support.v4.app.Fragment getItem(int position) {
        switch (position) {
            case 0:  return ListFragment1.newInstance();
            case 1:  return ListFragment2.newInstance();
            default: return null;
        }
    }
}
顺便说一下,
vpPager.setOffscreenPageLimit(2)
是无用的,因为您只有2个页面,而这是一种我从未使用过的方法,即使我有很多片段要管理,因为它需要内存

关于更新:删除与ViewPager处理片段相关的任何逻辑。如果需要在片段内启动异步任务,可以使用片段生命周期的方法之一:onResume()、onCreateView()等等

class IPagedFragment extends Fragment {

    public void onResume() {
        super.onResume();
        myTask= new MyTask();
        myTask.execute((Void)null);
    }
}
请删除私有最终ListFragment1 lf1=ListFragment1.newInstance()。相信我,这不是一个好主意,因为你有一个强有力的参考你的片段


我构建了一个简单的项目,您可以将其用作参考实现。您可以从my下载源代码。

您必须使用
ChildFragmentManager
如下所示

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    pagerAdapter = new PagerAdapter(getChildFragmentManager()); //here used child fragment manager 
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragmentA, container, false);

    vpPager = (ViewPager)view.findViewById(R.id.vpPager);
    vpPager.setOffscreenPageLimit(2);
    vpPager.setAdapter(pagerAdapter);
    vpPager.addOnPageChangeListener(this);

    return view;
}
在我使用viewpager和fragment的代码中,它的工作方式很有魅力

  • 在PagerAdapter类上重写该方法, 当传呼机发生变化时,我会尝试一下
  • 我将创建类似于:

     private class PagerAdapter extends FragmentPagerAdapter {
            private final ListFragment1 lf1 = ListFragment1 .newInstance();
            private final ListFragment2 lf2 = ListFragment2 .newInstance();
    
            public PagerAdapter(android.support.v4.app.FragmentManager fm) {
                super(fm);
            }
    
            @Override
            public android.support.v4.app.Fragment getItem(int position) {
                switch (position) {
                    case 0:  return lf1;
                    case 1:  return lf2;
                    default: return null;
                }
            }
    
            @Override
            public int getCount() {
                return 2;
            }
    
            @Override
            public void setPrimaryItem(ViewGroup container, int position, Object object) {
                super.setPrimaryItem(container, position, object);
                if (position == 0)
                    lf1.updateUI(); //Refresh what you need on this fragment
                else if (position == 1)
                    lf2.updateUI();
            }
    
        }
    
  • 您还缺少
    getCount()
  • 我不确定屏幕外是否有任何用处,但这可能不是问题<代码>vpPager.setOffscreenPageLimit(2)
  • 还有一件事,我还要删除
    vpPager.addOnPageChangeListener(this)
    ,这没有用,因为它可能会给您带来一些问题。 无论您需要做什么,您都可以在没有它的情况下完成,通过覆盖分页,您可能会“破坏”一些标准分页(因为没有调用super)

  • 如果上述任何解决方案不起作用,您可以尝试通过向寻呼机视图实例发布(延迟)适配器的附加notifyDataSetChanged调用来解决:

    vpPager.post(new Runnable() {
        @Override
        public void run() {
            pagerAdapter.notifyDataSetChanged();
        }
    });
    


    刚才我用
    getChildFragmentManager()
    将此作为参数传递给pagerAdapter。它会起作用的

    在片段使用中使用pagerAdapter时:

    PagerAdapter adapter = new PagerAdapter(getChildFragmentManager());
    
    如果是活动,请使用getFragmentManager()


    使用getChildFragmentManager()而不是supportFragmentManager()

    如果您在Kotlin中遇到这种情况,就会是这样

    val fragmentAdapter = FragmentPageAdapter(childFragmentManager)
    

    这个覆盖应该做什么?不管怎么说,我试过了,但问题没有解决。我在viewpager片段中启动了一个异步任务,得到了一个
    非法状态异常:未附加到活动
    ,这是另一个问题。发布你的答案,我已经更新了我的答案。顺便说一句,你必须记住的一般规则是每个班级都有自己的规则和能力。从AsyncTask获得的数据用于在片段内部构建一些视图?然后让片段创建异步任务!我还向dropbox公共区域添加了一个简单的项目,以展示如何正确地执行您需要执行的操作。我非常抱歉,这种解决方案已被接受。我不明白为什么你认为可以保留你的记忆片段。这是一种糟糕的设计模式,因为每个片段都需要系统无法释放的内存,例如,当您想切换到FragmentStatePagerAdapter时。我只是遵循他的设计并指出了改进的方法,您也可以这样做