#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