android - ViewPager en el Fragmento pierde su contenido cuando se vuelve a mostrar el Fragmento
android-fragments android-viewpager (1)
Tengo un fragmento simple con un ViewPager.
Estoy usando la biblioteca de soporte actualizada, v4 rev18!
Si lo muestro por primera vez, todo funciona bien, si vuelvo y lo muestro de nuevo, la aplicación falla con la siguiente excepción:
java.lang.NullPointerException
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:569)
at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:211)
at android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:1281)
at android.view.View.dispatchRestoreInstanceState(View.java:12043)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2688)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2694)
at android.view.View.restoreHierarchyState(View.java:12021)
at android.support.v4.app.Fragment.restoreViewState(Fragment.java:425)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:949)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1460)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4800)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:798)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:565)
at dalvik.system.NativeStart.main(Native Method)
Mi clase simple parece seguir y debería estar, en realidad, bien ...
public class RoutineDayFragment extends BaseFragment
{
private RoutinesActivity mParent = null;
@InjectView(R.id.pager)
MyViewPager pager = null;
@InjectView(R.id.indicator)
PagerSlidingTabStrip indicator = null;
private FragmentStatePagerAdapter mAdapter = null;
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
mParent = ((RoutinesActivity) getBaseActivity());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.view_pager, container, false);
Views.inject(this, v);
mAdapter = new FragmentStatePagerAdapter(getChildFragmentManager())
{
@Override
public int getCount()
{
if (mParent.getSharedData().selectedRoutine == -1)
return 0;
return mParent.getSharedData().routines.get(mParent.getSharedData().selectedRoutine).getRWorkoutDay().size();
}
@Override
public Fragment getItem(int pos)
{
return DayFragment.newInstance(pos);
}
@Override
public CharSequence getPageTitle(int pos)
{
return Common.getString(R.string.day) + (pos + 1);
}
};
pager.setAdapter(mAdapter);
indicator.setViewPager(pager);
return v;
}
}
Use una AsyncTask para establecer el ViewPagerAdapter:
private class SetAdapterTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
return null;
}
@Override
protected void onPostExecute(Void result) {
if(mAdapter != null) mViewPager.setAdapter(mAdapter);
}
}
Llámalo así:
mAdapter = new PageAdapter(getChildFragmentManager());
new SetAdapterTask().execute();
Y reinicie el adaptador en el método onResume(
) de su Fragmento.
ACTUALIZACIÓN - ¿Cómo anidar un ViewPager dentro de un fragmento?
Muy bien, aquí está. Modifiqué el ejemplo de Google de la navegación efectiva para que se ajuste a sus inquietudes.
¿Qué he creado?
- Creé una aplicación simple, que contiene un MainActivity con un ViewPager y 3 pestañas.
- Cada una de estas pestañas está representada por un fragmento que también contiene un ViewPager.
- El ViewPager dentro del Fragmento contiene 10 páginas.
- Así que tenemos 3 pestañas / fragmentos "principales", cada uno con 10 fragmentos más.
- Para fines de demostración, hice que el ViewPager de nivel superior no fuera deslizable , así que tienes que usar las pestañas para cambiar entre los Fragmentos principales (creé un CustomViewPager e hice algunos cambios para eliminar la capacidad de deslizamiento de ViewPagers)
- El ViewPager dentro de los Fragmentos principales se puede deslizar, así que puedes deslizar para cambiar entre los sub-Fragmentos, y presionar las pestañas para cambiar entre los Fragmentos principales
- Si hace clic en un subgrupo, se inicia una nueva actividad.
- Si regresa a la actividad anterior cuando se cierra la nueva actividad, se conserva el estado de Fragmentos y ViewPager.
- Al cambiar los fragmentos principales, su estado también se conserva
Aquí está la actividad principal. Java
Contiene dos adaptadores, uno para los Fragmentos principales, uno para los Sub Fragmentos. Además, hay dos clases de Fragmentos, una es el Fragmento que contiene el ViewPager (el fragmento principal), la otra es el Sub-Fragmento (dentro del ViewPager anidado)
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
AppSectionsPagerAdapter mAppSectionsPagerAdapter;
NonSwipeableViewPager mViewPager;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAppSectionsPagerAdapter = new AppSectionsPagerAdapter(getSupportFragmentManager());
// Set up the action bar.
final ActionBar actionBar = getActionBar();
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Set up the ViewPager, attaching the adapter and setting up a listener for when the
// user swipes between sections.
mViewPager = (NonSwipeableViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mAppSectionsPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mAppSectionsPagerAdapter.getCount(); i++) {
actionBar.addTab(
actionBar.newTab()
.setText(mAppSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {}
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {}
public static class AppSectionsPagerAdapter extends FragmentPagerAdapter {
public AppSectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
switch (i) {
default:
// The other sections of the app are dummy placeholders.
Fragment fragment = new ViewPagerContainerFragment();
return fragment;
}
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
return "Tab " + (position +1);
}
}
public static class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
switch (i) {
default:
// The other sections of the app are dummy placeholders.
Fragment fragment = new ViewPagerFragment();
Bundle b = new Bundle();
b.putString("key", "I am fragment nr " + i);
fragment.setArguments(b);
return fragment;
}
}
@Override
public int getCount() {
return 10;
}
}
/**
* THIS FRAGMENT CONTAINS A VIEWPAGER
*/
public static class ViewPagerContainerFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.main_fragment, container, false);
ViewPager pager = (ViewPager) rootView.findViewById(R.id.nestedViewPager);
pager.setAdapter(new ViewPagerAdapter(getChildFragmentManager()));
return rootView;
}
}
/**
* THIS FRAGMENT IS INSIDE THE VIEWPAGER
*/
public static class ViewPagerFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.sub_fragment, container, false);
((TextView) rootView.findViewById(R.id.tv1)).setText(getArguments().getString("key"));
((TextView) rootView.findViewById(R.id.tv1)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), CollectionDemoActivity.class);
startActivity(intent);
}
});
return rootView;
}
}
}
activity_main.xml
<com.example.android.effectivenavigation.NonSwipeableViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
main_fragment.xml
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nestedViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="24sp" />
sub_fragment.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="50sp" />
El resultado final se ve así:
Tenemos 3 fragmentos de nivel superior, cada uno de los cuales contiene un ViewPager con 10 fragmentos.