我一直看到我的ViewPager和我自己的FragmentStatePagerAdapter有一些奇怪的行为。
我的视图层次结构如下:
-> (1) Fragment root view (RelativeLayout)
-> (2) ViewPager
-> (3) ViewPager's current fragment view
当负责片段根视图(1)的片段被隐藏(在片段事务中使用.hide())然后显示(使用.show())时,当前显示在片段视图中的片段视图尽管片段仍然存在,但ViewPager(3)变为null。基本上,我的ViewPager变得完全空白/透明。
我找到解决此问题的唯一方法是致电
int current = myViewPager.getCurrentItem();
myViewPager.setAdapter(myAdapter);
myViewPager.setCurrentItem(current);
显示父片段后的。这会以某种方式触发视图重新创建并显示在屏幕上。不幸的是,这偶尔会导致异常处理寻呼机适配器,在旧观察者上调用unregisterDataSetObserver()
两次。
有更好的方法吗?我想我要问的是:
当隐藏ViewPager的父片段时,为什么我的ViewPager中的片段视图会被销毁?
更新:当应用程序“最小化”然后“恢复”(通过按主页操作键然后返回)时,也会发生这种情况。
根据请求,这是我的寻呼机适配器类:
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);
}
}
我已经重命名了一些变量,但除此之外,它正是我所拥有的。
答案 0 :(得分:87)
您没有为特定问题提供足够的信息,因此我构建了一个示例项目,尝试重现您的问题:该应用有一个活动,在相对布局中保存片段(PagerFragment
)这个布局我有一个隐藏&amp; amp;显示在PagerFragment
之上。 PagerFragment
有ViewPager
,寻呼机适配器中的每个片段只显示一个标签 - 此片段名为DataFragment
。标签列表在父活动中创建,并传递给PagerFragment,然后通过其适配器传递给每个DataFragment
。更改PagerFragment
可见性没有任何问题,每次再次可见时,它会显示之前显示的标签。
问题的关键: 在创建viewpager适配器时使用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();
}
}
此适配器为每个页面创建 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>
享受编码!
答案 1 :(得分:18)
也许它会有所帮助mViewPager.setOffscreenPageLimit(5)
设置应保留在页面两侧的页数 处于空闲状态的视图层次结构中的当前页面。页面超出此范围 在需要时,将从适配器重新创建限制。
这是作为优化提供的。如果您事先知道这个号码 您将需要支持或具有延迟加载机制的页面 放在你的页面上,调整这个设置可以带来好处 感知到的分页动画和交互的平滑性。如果你有 少量页面(3-4),您可以一次保持活动状态, 在新创建的视图子树的布局中花费的时间会减少 用户来回翻页。
您应该保持此限制,特别是如果您的网页很复杂 布局。此设置默认为1.
答案 2 :(得分:4)
View Pager非常坚定地保持其片段始终保持新鲜,从而通过在不使用片段时释放内存来优化性能。显然,这是移动系统中有用的有用特性。但由于资源的持续释放,每次获得焦点时都会创建片段。
mViewPager.setOffscreenPageLimit(NUMBEROFFRAGMENTSCREENS);
此Old Post为您的问题提供了一个有趣的解决方案..请参阅
答案 3 :(得分:1)
我遇到了同样的问题。我的应用程序(FragmentActivity)有一个包含3个framgents的寻呼机(ViewPager)。在片段之间滑动时,它们会被破坏并重新创建。实际上它在功能上没有问题(期望未封闭的游标),但我也想知道这个问题。
我不知道是否有改变ViewPager行为的解决方法,但我建议有一个配置对象(可能是静态开启),然后在销毁之前将myViewPager
对象保存在配置对象中。
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;
}
通过这种方式,您可以保存对不会被销毁的对象的引用,因此可以引用它,您可以重新加载所有关键对象。
我希望你觉得我的建议很有用!
答案 4 :(得分:1)
对我来说,我改为getChildFragmentManager()
而不是getFragmentManager()
而且效果很好。
的实施例强>
pagerAdapt = new PagerAdapt(getChildFragmentManager());