Intereting Posts
Кнопка включения, когда EditText имеет текст (RxAndroid) Применение эффектов EffectFactory для CameraPreview Импортировать существующий проект Android в Eclipse: нет исходной папки? Android Studio 2.2 Preview 1 Проблемы с рендерингом Android Floating Action Button Полупрозрачный цвет фона Почему мои определения определения огурца не определены в Android Studio Панель инструментов Android для всплывающих окон и темы Как показать на экране позицию курсора на планшете Android или смартфоне? In-app, сделайте пользовательский интерфейс Google Play прозрачным RecyclerView: как очистить кэшированные / переработанные виды? Android-градир, что это? Android webview loadDataWithBaseURL как загружать изображения из активов? Получение ошибки «не удается разрешить метод» при попытке реализовать getSharedPreferences в Android Studio Как применить анимацию fade-in / fade-out при замене фрагмента Использование контекстной панели действий с фрагментами

Двойной фрагмент, вращающийся Android с ActionBar

Я сделал простую деятельность Android с ActionBar для переключения между двумя фрагментами. Все нормально, пока я не вращаю устройство. В фактах, когда я вращаюсь, у меня есть 2 фрагмента один над другим: предыдущий активный и первый. Зачем? Если вращение разрушает и воссоздает мою деятельность, почему я получаю 2 фрагмента?

Образец кода:

Мероприятия

package rb.rfrag.namespace; import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.Activity; import android.os.Bundle; public class RFragActivity extends Activity { /** Called when the activity is first created. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Notice that setContentView() is not used, because we use the root // android.R.id.content as the container for each fragment // setup action bar for tabs final ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); //actionBar.setDisplayShowTitleEnabled(false); Tab tab; tab = actionBar.newTab() .setText(R.string.VarsTab) .setTabListener(new TabListener<VarValues>( this, "VarValues", VarValues.class)); actionBar.addTab(tab); tab = actionBar.newTab() .setText(R.string.SecTab) .setTabListener(new TabListener<SecFrag>( this, "SecFrag", SecFrag.class)); actionBar.addTab(tab); } } 

TabListener

 package rb.rfrag.namespace; import android.app.ActionBar; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.ActionBar.Tab; public class TabListener<T extends Fragment> implements ActionBar.TabListener { private Fragment mFragment; private final Activity mActivity; private final String mTag; private final Class<T> mClass; /** Constructor used each time a new tab is created. * @param activity The host Activity, used to instantiate the fragment * @param tag The identifier tag for the fragment * @param clz The fragment's Class, used to instantiate the fragment */ public TabListener(Activity activity, String tag, Class<T> clz) { mActivity = activity; mTag = tag; mClass = clz; } /* The following are each of the ActionBar.TabListener callbacks */ public void onTabSelected(Tab tab, FragmentTransaction ft) { // Check if the fragment is already initialized if (mFragment == null) { // If not, instantiate and add it to the activity mFragment = Fragment.instantiate(mActivity, mClass.getName()); ft.add(android.R.id.content, mFragment, mTag); } else { // If it exists, simply attach it in order to show it ft.attach(mFragment); } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (mFragment != null) { // Detach the fragment, because another one is being attached ft.detach(mFragment); } } } 

Solutions Collecting From Web of "Двойной фрагмент, вращающийся Android с ActionBar"

Я решил использовать onSaveInstanceState и onRestoreInstanceState в Activity для сохранения выбранной вкладки и изменения onTabSelected следующим образом.

Последнее изменение избегает того, что Android восстанавливает фрагмент, который он не разрушает. Однако я не понимаю, почему активность разрушена событием поворота, а текущий фрагмент no. (У вас есть представление об этом?)

 public void onTabSelected(Tab tab, FragmentTransaction ft) { // previous Fragment management Fragment prevFragment; FragmentManager fm = mActivity.getFragmentManager(); prevFragment = fm.findFragmentByTag(mTag); if (prevFragment != null) { mFragment = prevFragment; } // \previous Fragment management // Check if the fragment is already initialized if (mFragment == null) { // If not, instantiate and add it to the activity mFragment = Fragment.instantiate(mActivity, mClass.getName()); ft.add(android.R.id.content, mFragment, mTag); } else { // If it exists, simply attach it in order to show it ft.attach(mFragment); } } 

Поскольку я использую android.support.v4.view.ViewPager переопределяющий onTabSelected , не поможет. Но все же ты намекнул мне в правильном направлении.

onSaveInstanceState android.support.v4.app.FragmentManager сохраняет все фрагменты в onSaveInstanceState android.support.v4.app.FragmentActivity . Игнорирование setRetainInstance В зависимости от вашего дизайна это может привести к дублированию фрагментов.

Самое простое решение – удалить сохраненный фрагмент в orCreate из действия:

  @Override public void onCreate (final android.os.Bundle savedInstanceState) { if (savedInstanceState != null) { savedInstanceState.remove ("android:support:fragments"); } // if super.onCreate (savedInstanceState); … return; } // onCreate 

Решение на самом деле не требует большой работы, следующие шаги гарантируют, что при повороте экрана сохраняется вкладка. Я столкнулся с перекрывающимися фрагментами, потому что при повороте экрана была выбрана первая вкладка, а не вторая, которая была выбрана до поворота экрана, и, следовательно, первая вкладка перекрывала содержимое второй вкладки.

Вот как должна выглядеть ваша деятельность (я использую ActionBarSherlock, но ее настройка должна быть очень простой):

 public class TabHostActivity extends SherlockFragmentActivity { private static final String SELETED_TAB_INDEX = "tabIndex"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Setup the action bar ActionBar actionBar = getSupportActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // Create the Tabs you need and add them to the actionBar... if (savedInstanceState != null) { // Select the tab that was selected before orientation change int index = savedInstanceState.getInt(SELETED_TAB_INDEX); actionBar.setSelectedNavigationItem(index); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save the index of the currently selected tab outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition()); } } 

И вот что выглядит мой ActionBar.TabListener (его частный класс в вышеуказанной деятельности):

  private class MyTabsListener<T extends Fragment> implements ActionBar.TabListener { private Fragment fragment; private final SherlockFragmentActivity host; private final Class<Fragment> type; private String tag; public MyTabsListener(SherlockFragmentActivity parent, String tag, Class type) { this.host = parent; this.tag = tag; this.type = type; } @Override public void onTabSelected(Tab tab, FragmentTransaction transaction) { /* * The fragment which has been added to this listener may have been * replaced (can be the case for lists when drilling down), but if the * tag has been retained, we should find the actual fragment that was * showing in this tab before the user switched to another. */ Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag); // Check if the fragment is already initialised if (currentlyShowing == null) { // If not, instantiate and add it to the activity fragment = SherlockFragment.instantiate(host, type.getName()); transaction.add(android.R.id.content, fragment, tag); } else { // If it exists, simply attach it in order to show it transaction.attach(currentlyShowing); } } public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) { /* * The fragment which has been added to this listener may have been * replaced (can be the case for lists when drilling down), but if the * tag has been retained, we should find the actual fragment that's * currently active. */ Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag); if (currentlyShowing != null) { // Detach the fragment, another tab has been selected fragmentTransaction.detach(currentlyShowing); } else if (this.fragment != null) { fragmentTransaction.detach(fragment); } } public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) { // This tab is already selected } 

Вышеупомянутая реализация также позволяет заменять фрагменты внутри вкладки на основе их тегов. Для этой цели при переключении фрагментов на одной и той же вкладке я использую одно и то же имя тега, которое использовалось для исходной структуры, которая была добавлена ​​на вкладку.

Спасибо Martin и asclepix за их решения. У меня есть 3 вкладки, а первая вкладка содержит 2 фрагмента, например:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" tools:context=".MainActivity" > <FrameLayout android:id="@+id/frActiveTask" android:layout_width="fill_parent" android:layout_height="50dp" android:layout_alignParentBottom="true" /> <FrameLayout android:id="@+id/frTaskList" android:layout_width="fill_parent" android:layout_height="match_parent" android:layout_alignParentTop="true" android:layout_above="@id/frActiveTask" /> </RelativeLayout> 

Использование onRestoreInstanceState , onSaveInstanceState и savedInstanceState.remove("android:support:fragments"); Методы и оператор работают почти отлично, за исключением того, что если активная вкладка не первая и вращается и сначала нажимает, появляется ясный экран, и только для второго щелчка на первой вкладке отображается нужный фрагмент. После отладки кода я понял, что первый addTab всегда вызывает событие onTabSelected в прослушивателе вкладок, с методом add фрагментов, а затем, когда setSelectedNavigationItem из onRestoreInstanceState detach выполняется на первой вкладке и add для другого. Это ненужное add вызова фиксируется в моем решении.

Моя деятельность

 protected void onCreate(Bundle savedInstanceState) { boolean firstTabIsNotAdded = false; if (savedInstanceState != null) { savedInstanceState.remove("android:support:fragments"); firstTabIsNotAdded = savedInstanceState.getInt(SELETED_TAB_INDEX) != 0; } super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // codes before adding tabs actionBar = getSupportActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); tabStartAndStop = actionBar.newTab().setText(getString(R.string.tab_title_start_and_stop)) .setTabListener( new FragmentTabListener<StartStopFragment>(this, getString(R.string.tab_title_start_and_stop_id), StartStopFragment.class, firstTabIsNotAdded)); tabHistory = actionBar.newTab().setText(getString(R.string.tab_title_history)) .setTabListener( new FragmentTabListener<HistoryFragment>(this, getString(R.string.tab_title_history_id), HistoryFragment.class, false)); tabRiporting = actionBar.newTab().setText(getString(R.string.tab_title_reporting)) .setTabListener( new FragmentTabListener<ReportingFragment>(this, getString(R.string.tab_title_reporting_id), ReportingFragment.class, false)); actionBar.addTab(tabStartAndStop); actionBar.addTab(tabHistory); actionBar.addTab(tabRiporting); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { if (savedInstanceState != null) { int index = savedInstanceState.getInt(SELETED_TAB_INDEX); actionBar.setSelectedNavigationItem(index); } super.onRestoreInstanceState(savedInstanceState); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save the index of the currently selected tab outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition()); } 

И измененный прослушиватель вкладки

 public class FragmentTabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener { private Fragment mFragment; private final Activity mFragmentActivity; private final String mTag; private final Class<T> mClass; private boolean doNotAdd; /** Constructor used each time a new tab is created. * @param activity The host Activity, used to instantiate the fragment * @param tag The identifier tag for the fragment * @param clz The fragment's Class, used to instantiate the fragment */ public FragmentTabListener(Activity activity, String tag, Class<T> clz, boolean doNotAdd) { mFragmentActivity = activity; mTag = tag; mClass = clz; this.doNotAdd = doNotAdd; } /* The following are each of the ActionBar.TabListener callbacks */ public void onTabSelected(Tab tab, FragmentTransaction ft) { // Check if the fragment is already initialized if (mFragment == null) { // If not, instantiate and add it to the activity if(doNotAdd){ doNotAdd = false; }else{ mFragment = Fragment.instantiate(mFragmentActivity, mClass.getName()); ft.add(android.R.id.content, mFragment, mTag); } } else { // If it exists, simply attach it in order to show it ft.attach(mFragment); } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (mFragment != null) { // Detach the fragment, because another one is being attached ft.detach(mFragment); } } public void onTabReselected(Tab tab, FragmentTransaction ft) { // User selected the already selected tab. Usually do nothing. } }