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

时间:2015-07-24 09:48:39

标签: android android-fragments android-viewpager

我有3个片段(A,B,C)的活动。片段A由ViewPagerListFragments组成。用户可以点击任何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;
        }
    }
}

第一次显示活动时,会正确显示viewpager列表片段。

2个viewpager片段从db加载数据,我只执行一次(创建片段时)。

用户可以点击某个项目并显示片段B.如果用户按下Back,则显示片段A.但是,列表片段未显示(已经存在它们的实例)。

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

这里有什么问题?有更好的方法吗?

编辑

如果我在寻呼机适配器中使用newInstance,我会得到IllegalStateException: not attached to activity。这是因为我按如下方式启动异步任务:

@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等)。

9 个答案:

答案 0 :(得分:72)

您必须使用下面的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就像魅力一样。

答案 1 :(得分:15)

刚才我在使用getChildFragmentManager()奋斗一整天后解决了这个问题 将此作为参数传递给pagerAdapter。它会起作用。

在片段使用中使用pagerAdapter:

PagerAdapter adapter = new PagerAdapter(getChildFragmentManager());

并且在活动的情况下使用getFragmentManager()

PagerAdapter adapter = new PagerAdapter(getFragmentManager());

答案 2 :(得分:13)

您正在使用Activity FragmentManager创建ListFragment1和ListFragment2,而您应该使用Fragment FragmentManager。因此,请使用pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager());修改pagerAdapter = new PagerAdapter(getChildFragmentManager());。通过这种方式,视图寻呼机的片段将被绑定。到托管viewpager的片段。此外,您不应该在viewpager中保留对片段的任何引用:它是Android已经管理的东西。试试:

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相关的任何逻辑。如果需要在Fragment中启动AsyncTask,可以使用Fragment生命周期的一种方法:onResume(),onCreateView()等等。

class IPagedFragment extends Fragment {

    public void onResume() {
        super.onResume();
        myTask= new MyTask();
        myTask.execute((Void)null);
    }
}

请取消private final ListFragment1 lf1 = ListFragment1 .newInstance();。相信我,这不是一个好主意,因为你有一个强烈的参考你的碎片。

我已经构建了一个可以用作参考实现的简单项目。您可以从我的dropbox下载源代码。

答案 3 :(得分:0)

尝试覆盖FragmentPagerAdapter中的getItemPosition方法:

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

答案 4 :(得分:0)

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

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

vpPager.postDelayed(new Runnable() {
    @Override
    public void run() {
        pagerAdapter.notifyDataSetChanged();
    }
}, 100 /* you have to find out the best delay time by trying/adjusting */);

答案 5 :(得分:0)

使用getChildFragmentManager()而不是supportFragmentManager()

答案 6 :(得分:0)

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

val fragmentAdapter = FragmentPageAdapter(childFragmentManager)

答案 7 :(得分:-1)

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

@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源代码(检查instantiateItem()方法): https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v13/java/android/support/v13/app/FragmentPagerAdapter.java

另见这个答案: keep instances of fragments inside FragmentPagerAdapter

答案 8 :(得分:-3)

  1. 在PagerAdapter类上重写方法setPrimaryItem, 当传呼机发生变化时会调用它,我会试一试。
  2. 我会创建类似的东西:

     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();
            }
    
        }
    
    1. 您也错过了getCount()
    2. 我不确定屏幕外是否有用,但它可能不是问题。 vpPager.setOffscreenPageLimit(2)
    3. 还有一件事,我也会删除vpPager.addOnPageChangeListener(this),对此没有用处,这可能会导致一些问题。 无论你需要做什么,你都可以在没有它的情况下将它拉下来,通过压倒分页,你可能会毁掉它。一些标准的分页(因为超级没有被称为)