当ViewPager的父代片段隐藏然后显示ViewPager的片段的视图丢失

我已经看到我的ViewPager与我自己的FragmentStatePagerAdapter一些奇怪的行为。

我的视图层次结构如下所示:

-> (1) Fragment root view (RelativeLayout) -> (2) ViewPager -> (3) ViewPager's current fragment view 

当负责碎片根视图(1)的碎片被隐藏(在片段事务中使用.hide()),然后显示(使用.show())时,当前显示在ViewPager(3 )变成空,虽然片段仍然存在。 基本上,我的ViewPager变得完全空白/透明。

我发现解决这个问题的唯一方法就是打电话

 int current = myViewPager.getCurrentItem(); myViewPager.setAdapter(myAdapter); myViewPager.setCurrentItem(current); 

显示父代片段之后。 这以某种方式触发视图被重新创build并出现在屏幕上。 不幸的是,这偶尔会导致exception处理寻呼机适配器在旧的观察者上调用两次unregisterDataSetObserver()

有一个更好的方法吗? 我想我问的是:

为什么ViewPager中的片段视图在ViewPager的父片段被隐藏时会被破坏?

更新:当应用程序“最小化”,然后“恢复”(通过按住home键,然后返回),也会发生这种情况。

每个请求,这是我的寻呼机适配器类:

 public class MyInfoSlidePagerAdapter extends FragmentStatePagerAdapter { private ArrayList<MyInfo> infos = new ArrayList<MyInfo>(); public MyInfoSlidePagerAdapter (FragmentManager fm) { super(fm); } public MyInfoSlidePagerAdapter (FragmentManager fm, MyInfo[] newInfos) { super(fm); setInfos(newInfos); } @Override public int getItemPosition(Object object) { int position = infos.indexOf(((MyInfoDetailsFragment)object).getMyInfo()); return position > 0 ? position : POSITION_NONE; } @Override public CharSequence getPageTitle(int position) { return infos.get(position).getName(); } @Override public Fragment getItem(int i) { return infos.size() > 0 ? MyInfoDetailsFragment.getNewInstance(infos.get(i)) : null; } @Override public int getCount() { return infos.size(); } public Location getMyInfoAtPosition(int i) { return infos.get(i); } public void setInfos(MyInfo[] newInfos) { infos = new ArrayList<MyInfo>(Arrays.asList(newInfos)); } public int getPositionOfMyInfo(MyInfo info) { return infos.indexOf(info); } } 

我已经重命名了一些variables,但除此之外,这正是我所拥有的。

您没有为您的具体问题提供足够的信息,所以我构build了一个试图重现您的问题的示例项目:应用程序有一个活动,在相对布局中包含一个片段( PagerFragment ),在此布局下面,我有一个button隐藏和显示上面的PagerFragmentPagerFragment有一个ViewPager ,pager适配器中的每个片段只是显示一个标签 – 这个片段被命名为DataFragment 。 标签列表在父活动中创build并传递给PagerFragment,然后通过其适配器传递给每个DataFragment 。 更改PagerFragment可见性完成时没有问题,每当它再次变为可见时,就会显示前面显示的标签。

问题的关键:在创buildviewpager适配器时使用Fragment#getChildFragmentManager()而不是getFragmentManager!

也许你可以比较这个简单的项目与你有什么,并检查差异在哪里。 所以这里(自上而下):

PagerActivity (项目中唯一的活动):

 public class PagerActivity extends FragmentActivity { private static final String PAGER_TAG = "PagerActivity.PAGER_TAG"; @Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstance); setContentView(R.layout.pager_activity); if (savedInstance == null) { PagerFragment frag = PagerFragment.newInstance(buildPagerData()); FragmentManager fm = getSupportFragmentManager(); fm.beginTransaction().add(R.id.layout_fragments, frag, PAGER_TAG).commit(); } findViewById(R.id.btnFragments).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { changeFragmentVisibility(); } }); } private List<String> buildPagerData() { ArrayList<String> pagerData = new ArrayList<String>(); pagerData.add("Robert de Niro"); pagerData.add("John Smith"); pagerData.add("Valerie Irons"); pagerData.add("Metallica"); pagerData.add("Rammstein"); pagerData.add("Zinedine Zidane"); pagerData.add("Ronaldo da Lima"); return pagerData; } protected void changeFragmentVisibility() { Fragment frag = getSupportFragmentManager().findFragmentByTag(PAGER_TAG); if (frag == null) { Toast.makeText(this, "No PAGER fragment found", Toast.LENGTH_SHORT).show(); return; } boolean visible = frag.isVisible(); Log.d("APSampler", "Pager fragment visibility: " + visible); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (visible) { ft.hide(frag); } else { ft.show(frag); } ft.commit(); getSupportFragmentManager().executePendingTransactions(); } } 

其布局文件pager_activity.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="4dp" > <Button android:id="@+id/btnFragments" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Hide/Show fragments" /> <RelativeLayout android:id="@+id/layout_fragments" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/btnFragments" android:layout_marginBottom="4dp" > </RelativeLayout> </RelativeLayout> 

观察到我在第一次显示活动时添加了PagerFragment – 以及PagerFragment类:

 public class PagerFragment extends Fragment { private static final String DATA_ARGS_KEY = "PagerFragment.DATA_ARGS_KEY"; private List<String> data; private ViewPager pagerData; public static PagerFragment newInstance(List<String> data) { PagerFragment pagerFragment = new PagerFragment(); Bundle args = new Bundle(); ArrayList<String> argsValue = new ArrayList<String>(data); args.putStringArrayList(DATA_ARGS_KEY, argsValue); pagerFragment.setArguments(args); return pagerFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); data = getArguments().getStringArrayList(DATA_ARGS_KEY); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.pager_fragment, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { pagerData = (ViewPager) view.findViewById(R.id.pager_data); setupPagerData(); } private void setupPagerData() { PagerAdapter adapter = new LocalPagerAdapter(getChildFragmentManager(), data); pagerData.setAdapter(adapter); } } 

它的布局(只有取得完整大小的ViewPager):

 <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pager_data" android:layout_width="match_parent" android:layout_height="match_parent" /> 

和它的适配器:

 public class LocalPagerAdapter extends FragmentStatePagerAdapter { private List<String> pagerData; public LocalPagerAdapter(FragmentManager fm, List<String> pagerData) { super(fm); this.pagerData = pagerData; } @Override public Fragment getItem(int position) { return DataFragment.newInstance(pagerData.get(position)); } @Override public int getCount() { return pagerData.size(); } } 

此适配器为每个页面创build一个DataFragment

 public class DataFragment extends Fragment { private static final String DATA_ARG_KEY = "DataFragment.DATA_ARG_KEY"; private String localData; public static DataFragment newInstance(String data) { DataFragment df = new DataFragment(); Bundle args = new Bundle(); args.putString(DATA_ARG_KEY, data); df.setArguments(args); return df; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); localData = getArguments().getString(DATA_ARG_KEY); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.data_fragment, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { view.findViewById(R.id.btn_page_action).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getActivity(), localData, Toast.LENGTH_SHORT).show(); } }); ((TextView) view.findViewById(R.id.txt_label)).setText(localData); } } 

DataFragment的布局:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="4dp" > <Button android:id="@+id/btn_page_action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Interogate" /> <TextView android:id="@+id/txt_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textAppearance="?android:attr/textAppearanceLarge" /> </RelativeLayout> 

享受编码!

也许它会帮助mViewPager.setOffscreenPageLimit(5)

在空闲状态下,将视图层次结构中应保留在当前页面任一侧的页数。 超出此限制的页面将在需要时从适配器重新创build。

这是作为一个优化提供的。 如果您事先知道需要支持的页面数量,或者在您的页面上放置了延迟加载机制,则调整此设置可以在分页animation和交互的感知平滑度方面带来好处。 如果您有一小部分页面(3-4)可以一次保持活动状态,那么随着用户来回翻页,新创build的视图子树的布局花费的时间将会减less。

你应该保持低限,特别是如果你的网页有复杂的布局。 该设置默认为1。

View Pager非常坚持保持其片段始终保持新鲜,从而在不使用片段时释放内存来优化性能。 显然这是移动系统中有效的有用特征。 但是由于这种资源的持续释放,每次获得焦点时都会创build片段。

 mViewPager.setOffscreenPageLimit(NUMBEROFFRAGMENTSCREENS); 

这里是文档。

这个老邮政有一个有趣的解决scheme为您的问题..请参阅

我有同样的问题。 我的应用程序(FragmentActivity)有一个传呼机(ViewPager)与3 framgents。 在碎片之间滑动时,它们一直被破坏和重新创build。 其实它在function上没有任何问题(期望未封闭的游标),但是我也想知道这个问题。

我不知道是否有一个解决方法来改变ViewPager的行为,但我build议有一个configuration对象(也许是一个静态),并在销毁之前保存myViewPager对象在configuration对象。

 public class App extends FragmentActivity { static MyData data; @Override protected void onCreate(Bundle savedInstanceState) { data = (MyData) getLastCustomNonConfigurationInstance(); if (data == null) { data = new MyData(); data.savedViewPager = myViewPager; } else { myViewPager = data.savedViewPager; } } @Override public Object onRetainCustomNonConfigurationInstance() { Log.d("onRetainCustomNonConfigurationInstance", "Configuration call"); return data; } } public class MyData { public ViewPager savedViewPager; } 

用这种方法,你可以保存对一个不会被破坏的对象的引用,因此可以引用它,你可以重载所有的关键对象。

我希望你find我的build议有用!