Как да възстановите дъщерни фрагменти в родителския фрагмент на TabHost/ViewPager

Проблемът е подчертан по-долу, първата част описва какво работи досега.

Използвам множество фрагменти с една дейност. В горната част на фигурата е показан единичен фрагмент, напр. списък с произволно съдържание. Ако щракна някъде, ще се отвори AboutTheAppFragment. Когато щракна върху back, отново се показва ASingleFragment. Това работи перфектно, но това беше лесната част :)

заета котка

Ето изрязания код, който използвах, за да отворя новия фрагмент.

AboutTheAppFragment aboutTheAppFragment = new AboutTheAppFragment();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(mContainer.getId(), aboutTheAppFragment, AboutTheAppFragment.class.getName());
fragmentTransaction.addToBackStack(null);

Сега е моят проблем

Вместо ASingleFragment обичам да показвам TabHostPagerFragment, който използва TabHost и ViewPager за показване на фрагментите (вижте фигурата по-долу). Да кажем, че имам двата фрагмента TabOneFragment и TabTwoFragment, но само един е показан във ViewPager. Това също работи чудесно, мога просто да плъзгам по екрана и той превключва между двата фрагмента на раздела. Ако обаче щракна някъде, за да отворя AboutTheAppFragment и щракна back отново, и двата фрагмента TabOneFragment и TabTwoFragment не показват нищо. Ако завъртя устройството, то презарежда/възстановява фрагментите отново и всичко работи добре. Така че въпросът ми е как мога да добавя TabOneFragment и TabTwoFragment към стека назад, така че да се показват правилно, когато щракна върху бутона back? Обърнете внимание, че работи като в сценарий 1, когато задам TabOneFragment като ASingleFragment, след това щракна някъде и щракна отново. Но не работи, когато използвам TabHostPagerFragment.

заета котка

Ето някои кодови фрагменти на TabHostPagerFragment:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the tabs pager fragment in the container
    View view = inflater.inflate(R.layout.fragment_tabs_pager, container, false);
    mContainer = container;

    // Set up the TabHost
    mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
    mTabHost.setup();

    // Set up the ViewPager
    mViewPager = (ViewPager) view.findViewById(R.id.pager);

    return view;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // Now initialize the TabsAdapter which will be used to manage the tabs
    mTabsAdapter = new TabsAdapter(activity, mTabHost, mViewPager);

    // Finally add the tabs to the TabsAdapter: meal plan for today and meal plan for the week 
    mTabsAdapter.addTab(mTabHost.newTabSpec("TabOne").setIndicator("TabOne", TabOneFragment.class, null);
    mTabsAdapter.addTab(mTabHost.newTabSpec("TabTwo").setIndicator("TabTwo", TabTwoFragment.class, null);

    if (savedInstanceState != null) {
        // NEVER REACHES THIS CASE
        mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
    }
}

Забелязах, че savedInstanceState винаги е null. Някаква идея как мога да поправя това и да съхраня разделите в състояние на запазен екземпляр?

Ще се радвам на всяка помощ.

Най-добри пожелания, Майкъл

РЕДАКТИРАНЕ:

Ето йерархията на извикванията на фрагментите:

09-25 09:19:25.695: V/TabsPagerParentFragment(1820): onPause
09-25 09:19:25.695: V/TabsPagerParentFragment(1820): onStop
09-25 09:19:25.703: V/TabsPagerParentFragment(1820): onDestroyView
09-25 09:19:25.710: V/AboutTheAppFragment(1820): onAttach
09-25 09:19:25.710: V/AboutTheAppFragment(1820): onCreate: savedInstanceState == null
09-25 09:19:25.710: V/AboutTheAppFragment(1820): onCreateView: savedInstanceState == null
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onViewCreated
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onActivityCreated
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onViewStateRestored
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onStart
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onResume
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onPause
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onStop
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onDestroyView
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onDestroy
09-25 09:19:27.312: V/TabsPagerParentFragment(1820): onCreateView: savedInstanceState == null
09-25 09:19:27.320: V/TabsPagerParentFragment(1820): onViewCreated
09-25 09:19:27.320: V/TabsPagerParentFragment(1820): onActivityCreated
09-25 09:19:27.328: V/TabsPagerParentFragment(1820): onViewStateRestored
09-25 09:19:27.328: V/TabsPagerParentFragment(1820): onStart
09-25 09:19:27.328: V/TabsPagerParentFragment(1820): onResume



Отговори (2)


Във вашия TabHostFragment конструкторите на TabOneFragment и TabTwoFragment извикват:

   setRetainInstance(true);

В момента нямате нищо, което да „реконструира“ състоянието на фрагментите. Когато замените ASingleFragment с AboutTheAppFragment, ASingleFragment се унищожава и забравя, докато не натиснете „назад“, в който момент системата ще пресъздаде наследството на изгледа, но нито едно от съдържанието им. Извикването на setRetainInstance(true) ще каже на системата да запази целия фрагмент, включително неговите данни, за да може да бъде възстановен.

saveInstanceState на onActivityCreated() не влиза в игра тук, това е за родителската активност, а не за дъщерните фрагменти.

person NeilS    schedule 25.09.2013
comment
Благодаря за вашия отговор! Опитах вашия подход, но не работи. Също така проверих дали всички конструктори се извикват. Копирах йерархията на извикванията в публикацията си - и се чудя защо методите onDestroy на TabOneFragment и TabTwoFragment никога не се извикват. Някаква идея? Между другото, внедрих и пейджър (за показване на снимки) без TabHost и това работи перфектно. Предполагам, че е нещо специфично за внедряването на TabHost :-/ - person Michael; 25.09.2013
comment
Изглежда, че TabHost не запазва състоянието си правилно. Ако сте направили setRetainInstance(true), тогава фрагментите не трябва да бъдат унищожени, така че fragment.onCreate() няма да бъде извикан. Но техните изгледи (tabhost) ще бъдат унищожени, така че се извиква onCreateView(). Добавяте разделите към адаптера в onActivityCreated(), но той създава нов TabAdapter, къде се присвоява това на viewPager? Бих се изкушил да преместя този код в onCreateView(), след което да извикам mViewPager.setAdapter(), само за да се уверя, че всичко е направено заедно. - person NeilS; 25.09.2013
comment
@NeilS как реши? аз също съм изправен пред същия проблем във viewpager+fragmetns - person Erum; 20.08.2015
comment
Имате ли точно същия проблем като първоначалния въпрос? Опитахте ли setRetainInstance(true)? Тъй като този въпрос е доста стар, може би е по-добре да създадете нов въпрос - свържете го тук и ще видя дали мога да помогна. - person NeilS; 20.08.2015

Имам сложно оформление с 3 раздела във фрагмент, което се изключва за други фрагменти. Разбрах, че ViewpagerAdapter ще запази състоянието, дори ако натиснете бутона за начало. Проблемът ми беше, че превключването назад и напред щеше да нулира елементите на изгледа на потребителския интерфейс на дъщерния фрагмент и да се срине. Ключът е да не new извадите ViewPagerAdapter. Добавянето на нулевата проверка за адаптера свърши работа за мен. Освен това не забравяйте да разпределите setOffscreenPageLimit() за вашите нужди. Освен това, доколкото разбирам, setRetainInstance(true); не трябва да се използва за фрагменти, които имат потребителски интерфейс, той е предназначен за безглави фрагменти.

Във фрагмента, който съдържа вашите раздели:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_tab, container, false);
    tabLayout = (TabLayout) view.findViewById(R.id.tablayout);
    viewPager = (ViewPager) view.findViewById(R.id.viewPager);

    //Important!!! Do not fire the existing adapter!!
    if (viewPagerAdapter == null) {
        viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
        viewPagerAdapter.addFragments(new AFragment(), "A");
        viewPagerAdapter.addFragments(new BFragment(), "B");
        viewPagerAdapter.addFragments(new CFragment(), "C");
    }
    //Allocate retention buffers for three tabs, mandatory
    viewPager.setOffscreenPageLimit(3);
    tabLayout.setupWithViewPager(viewPager);
    viewPager.setAdapter(viewPagerAdapter);

    return view;
}
person Len    schedule 10.05.2017