Транзакция между фрагментами только внутри одной вкладки ActionBar

#android #android-activity #tabs #android-fragments #android-actionbar

#Android #android-активность #вкладки #android-фрагменты #android-actionbar

Вопрос:

У меня есть приложение с тремя вкладками (вкладки ActionBar), каждая из которых содержит по одному фрагменту за раз.

TabListener

TabsActivity

Tab1 -> ListFragment1 -> ListFragment2 -> Fragment3

Tab2 -> Tab2Fragment

Tab3 -> Tab3Fragment

Проблема в том, что когда я создаю FragmentTransaction (внутри OnListItemClicked) из ListFragment1 в ListFragment2, фрагменты внутри других вкладок также меняются на ListFragment2.

В конце концов, я хочу изменить фрагменты только внутри вкладки on и сохранить состояние других вкладок.

Я уже сохраняю состояние (onSavedInstance()). Вы знаете, чего мне здесь не хватает?

Часть кода:

 public class TabsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tabs);

        // setup Action Bar for tabs
        ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // instantiate fragment for the tab
        Fragment networksFragment = new NetworksFragment();
        // add a new tab and set its title text and tab listener
        actionBar.addTab(actionBar.newTab().setText("Tab1")
                .setTabListener(new TabsListener(ListFragment1)));

        // instantiate fragment for the tab
        Fragment historyFragment = new HistoryFragment();
        // add a new tab and set its title text and tab listener
        actionBar.addTab(actionBar.newTab().setText("Tab2")
                .setTabListener(new TabsListener(Tab2Fragment)));

        // instantiate fragment for the tab
        Fragment settingsFragment = new SettingsFragment();
        // add a new tab and set its title text and tab listener
        actionBar.addTab(actionBar.newTab().setText("Tab3")
                .setTabListener(new TabsListener(Tab3Fragment)));
    }
}
  

 public class TabsListener implements ActionBar.TabListener {

    private Fragment frag;


    // Called to create an instance of the listener when adding a new tab
    public TabsListener(Fragment networksFragment) {
        frag = networksFragment;
    }

    @Override
    public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {    
        ft.add(R.id.fragment_container, frag, null);
    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        ft.remove(frag);        
    }
}
  

 public class ListFragment1 extends ListFragment {

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {

        getListView().setItemChecked(position, true);

        ListFragment2 fragment2 = ListFragment2.newInstance(position);

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.fragment_container, fragment2);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(null);
        ft.commit();
    }
}
  

Ответ №1:

Вы ничего не упускаете (или я тоже это упускаю).

Я долго и упорно искал способ сделать это «правильно», но я ничего не мог найти. В итоге я написал свою собственную логику backstack. К сожалению, мой работодатель владеет моим кодом, поэтому я не могу поделиться этим дословно, но вот мой подход:

Создайте enum по одной записи для каждой из ваших вкладок. Давайте назовем это TabType . Теперь создайте переменную tabStacks экземпляра типа HashMap<TabType, Stack<String>> . Теперь вы можете создать экземпляр одного стека для каждой вкладки — каждый стек представляет собой список тегов, как указано Fragment.getTag() . Таким образом, вам не нужно беспокоиться о сохранении ссылок Fragments и о том, исчезнут ли они у вас при повороте устройства. Каждый раз, когда вам нужна ссылка на a Fragment , возьмите нужный тег из стека и используйте FragmentManager.findFragmentByTag() .

Теперь всякий раз, когда вы хотите нажать a Fragment на вкладку, создайте новый тег (я использовал UUID.randomUUID().toString() ) и используйте его в своем вызове FragmentTransaction.add() . Затем нажмите тег поверх стека для отображаемой в данный момент вкладки.

Будьте осторожны: когда вы хотите поместить новый фрагмент поверх старого, не remove() используйте старый, так FragmentManager как он будет считать, что он исчез, и он будет очищен. Обязательно сделайте detach() это, а затем attach() позже. Используйте только remove() тогда, когда вы постоянно нажимаете a Fragment , и используйте только add() в первый раз, когда вы хотите его показать.

Затем вам нужно будет добавить относительно простую логику в свой TabListener . Когда вкладка не выбрана, просто peek() в ее стеке и detatch() связанном фрагменте. Когда выбрана вкладка, peek() вверху этого стека и attach() этого фрагмента.

Наконец, вам придется иметь дело с причудами жизненного цикла активности (например, изменениями ориентации). Сохраните свою Map часть Stacks , а также текущую выбранную вкладку и снова распакуйте ее в свой onCreate() . (Вы не получаете эту упаковку и распаковку бесплатно, но это довольно легко сделать.) К счастью, ваше TabType перечисление таково Serializable , что его должно быть тривиально поместить в Bundle .

Комментарии:

1. Большое спасибо! Ваш ответ — единственный в StackOverflow, который помог мне решить эту проблему.

2. Я не обязан работодателем, поэтому я создал суть, содержащую весь необходимый код: gist.github.com/4619215