Intereting Posts
Структура приложений Android: DLC через биллинг в приложении Как узнать, что устройство подключено к Wi-Fi или 3G, программно Разница между Intent.ACTION_GET_CONTENT и Intent.ACTION_PICK Создайте Google как мгновенный поиск в приложении для Android Список вложенных списков Android Можно ли отправлять пользовательские объекты в Android Wear? Android Notification.Builder: показать уведомление без значка Как остановить атаку hack / DOS на веб-API Функции Android USB? Невозможно выполнить dex: несколько файлов dex определяют разрешение Landroid / Manifest $; Nth-child (even) генерирует ParseException при использовании селектора jsoup в android? Чтение нескольких характеристик с устройства BLE синхронно (рекомендуемый метод для Android) Автоподключение к известному устройству BLE Покупка в приложении: получите имя или идентификатор учетной записи Google Play, запрашивающей покупку Преобразование ShaderToy в фрагментарный шейдер

Фрагмент onCreateView и onActivityCreated вызывается дважды

Я разрабатываю приложение с использованием Android 4.0 ICS и фрагментов.

Рассмотрим этот модифицированный пример из примера примера приложения API ICS 4.0.3 (API уровня 15):

public class FragmentTabs extends Activity { private static final String TAG = FragmentTabs.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final ActionBar bar = getActionBar(); bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); bar.addTab(bar.newTab() .setText("Simple") .setTabListener(new TabListener<SimpleFragment>( this, "mysimple", SimpleFragment.class))); if (savedInstanceState != null) { bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0)); Log.d(TAG, "FragmentTabs.onCreate tab: " + savedInstanceState.getInt("tab")); Log.d(TAG, "FragmentTabs.onCreate number: " + savedInstanceState.getInt("number")); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("tab", getActionBar().getSelectedNavigationIndex()); } public static class TabListener<T extends Fragment> implements ActionBar.TabListener { private final Activity mActivity; private final String mTag; private final Class<T> mClass; private final Bundle mArgs; private Fragment mFragment; public TabListener(Activity activity, String tag, Class<T> clz) { this(activity, tag, clz, null); } public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) { mActivity = activity; mTag = tag; mClass = clz; mArgs = args; // Check to see if we already have a fragment for this tab, probably // from a previously saved state. If so, deactivate it, because our // initial state is that a tab isn't shown. mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag); if (mFragment != null && !mFragment.isDetached()) { Log.d(TAG, "constructor: detaching fragment " + mTag); FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction(); ft.detach(mFragment); ft.commit(); } } public void onTabSelected(Tab tab, FragmentTransaction ft) { if (mFragment == null) { mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs); Log.d(TAG, "onTabSelected adding fragment " + mTag); ft.add(android.R.id.content, mFragment, mTag); } else { Log.d(TAG, "onTabSelected attaching fragment " + mTag); ft.attach(mFragment); } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (mFragment != null) { Log.d(TAG, "onTabUnselected detaching fragment " + mTag); ft.detach(mFragment); } } public void onTabReselected(Tab tab, FragmentTransaction ft) { Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show(); } } public static class SimpleFragment extends Fragment { TextView textView; int mNum; /** * When creating, retrieve this instance's number from its arguments. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(FragmentTabs.TAG, "onCreate " + (savedInstanceState != null ? ("state " + savedInstanceState.getInt("number")) : "no state")); if(savedInstanceState != null) { mNum = savedInstanceState.getInt("number"); } else { mNum = 25; } } @Override public void onActivityCreated(Bundle savedInstanceState) { Log.d(TAG, "onActivityCreated"); if(savedInstanceState != null) { Log.d(TAG, "saved variable number: " + savedInstanceState.getInt("number")); } super.onActivityCreated(savedInstanceState); } @Override public void onSaveInstanceState(Bundle outState) { Log.d(TAG, "onSaveInstanceState saving: " + mNum); outState.putInt("number", mNum); super.onSaveInstanceState(outState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(FragmentTabs.TAG, "onCreateView " + (savedInstanceState != null ? ("state: " + savedInstanceState.getInt("number")) : "no state")); textView = new TextView(getActivity()); textView.setText("Hello world: " + mNum); textView.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb)); return textView; } } 

}

Вот результат, полученный от запуска этого примера, а затем поворот телефона:

 06-11 11:31:42.559: D/FragmentTabs(10726): onTabSelected adding fragment mysimple 06-11 11:31:42.559: D/FragmentTabs(10726): onCreate no state 06-11 11:31:42.559: D/FragmentTabs(10726): onCreateView no state 06-11 11:31:42.567: D/FragmentTabs(10726): onActivityCreated 06-11 11:31:45.286: D/FragmentTabs(10726): onSaveInstanceState saving: 25 06-11 11:31:45.325: D/FragmentTabs(10726): onCreate state 25 06-11 11:31:45.340: D/FragmentTabs(10726): constructor: detaching fragment mysimple 06-11 11:31:45.340: D/FragmentTabs(10726): onTabSelected attaching fragment mysimple 06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate tab: 0 06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate number: 0 06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView state: 25 06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated 06-11 11:31:45.348: D/FragmentTabs(10726): saved variable number: 25 06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView no state 06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated 

Мой вопрос в том, почему вызов onCreateView и onActivityCreated дважды? Первый раз с Bundle с сохраненным состоянием и во второй раз с null savedInstanceState?

Это вызывает проблемы с сохранением состояния фрагмента при вращении.

Solutions Collecting From Web of "Фрагмент onCreateView и onActivityCreated вызывается дважды"

Я тоже почесывал голову об этом некоторое время, и поскольку объяснение Дейва немного сложно понять, я опубликую мой (видимо, рабочий) код:

 private class TabListener<T extends Fragment> implements ActionBar.TabListener { private Fragment mFragment; private Activity mActivity; private final String mTag; private final Class<T> mClass; public TabListener(Activity activity, String tag, Class<T> clz) { mActivity = activity; mTag = tag; mClass = clz; mFragment=mActivity.getFragmentManager().findFragmentByTag(mTag); } public void onTabSelected(Tab tab, FragmentTransaction ft) { if (mFragment == null) { mFragment = Fragment.instantiate(mActivity, mClass.getName()); ft.replace(android.R.id.content, mFragment, mTag); } else { if (mFragment.isDetached()) { ft.attach(mFragment); } } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (mFragment != null) { ft.detach(mFragment); } } public void onTabReselected(Tab tab, FragmentTransaction ft) { } } 

Как вы можете видеть, это в значительной степени похоже на образец Android, кроме того, что он не отсоединяется в конструкторе, и вместо этого вместо него нужно заменить вместо него .

После многократного исправления и пробной ошибки я обнаружил, что обнаружение фрагмента в конструкторе, по-видимому, вызывает проблему с двойным onCreateView (например, он заканчивается как null для onTabSelected при вызове через путь ActionBar.setSelectedNavigationItem (), когда Сохранение / восстановление состояния).

Хорошо, вот что я узнал.

Я не понял, что все фрагменты, связанные с активностью при изменении конфигурации (поворот телефона), воссоздаются и добавляются к активности. (Что имеет смысл)

То, что происходило в конструкторе TabListener, было закрыто, если оно было найдено и привязано к активности. Смотри ниже:

 mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag); if (mFragment != null && !mFragment.isDetached()) { Log.d(TAG, "constructor: detaching fragment " + mTag); FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction(); ft.detach(mFragment); ft.commit(); } 

Позже в действии onCreate ранее выбранная вкладка была выбрана из состояния сохраненного экземпляра. Смотри ниже:

 if (savedInstanceState != null) { bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0)); Log.d(TAG, "FragmentTabs.onCreate tab: " + savedInstanceState.getInt("tab")); Log.d(TAG, "FragmentTabs.onCreate number: " + savedInstanceState.getInt("number")); } 

Когда вкладка была выбрана, она будет повторно подключена в обратном вызове onTabSelected.

 public void onTabSelected(Tab tab, FragmentTransaction ft) { if (mFragment == null) { mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs); Log.d(TAG, "onTabSelected adding fragment " + mTag); ft.add(android.R.id.content, mFragment, mTag); } else { Log.d(TAG, "onTabSelected attaching fragment " + mTag); ft.attach(mFragment); } } 

Прикрепленный фрагмент является вторым вызовом методов onCreateView и onActivityCreated. (Первое, когда система воссоздает активность и все прикрепленные фрагменты). Первый раз, когда пакет onSavedInstanceState Bundle сохранил данные, но не во второй раз.

Решение состоит в том, чтобы не отделить фрагмент в конструкторе TabListener, просто оставьте его прикрепленным. (Вам все равно нужно найти его в FragmentManager по его тегу). Кроме того, в методе onTabSelected я проверяю, отсоединен ли фрагмент, прежде чем прикрепить его. Что-то вроде этого:

 public void onTabSelected(Tab tab, FragmentTransaction ft) { if (mFragment == null) { mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs); Log.d(TAG, "onTabSelected adding fragment " + mTag); ft.add(android.R.id.content, mFragment, mTag); } else { if(mFragment.isDetached()) { Log.d(TAG, "onTabSelected attaching fragment " + mTag); ft.attach(mFragment); } else { Log.d(TAG, "onTabSelected fragment already attached " + mTag); } } } 

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

В принципе, при использовании навигации по спискам “ onNavigationItemSelected () is automatically called when your activity is created/re-created, whether you like it or not. To prevent your Fragment's is automatically called when your activity is created/re-created, whether you like it or not. To prevent your Fragment's двойное from being called twice, this initial automatic call to onCreateView () from being called twice, this initial automatic call to onNavigationItemSelected () should check whether the Fragment is already in existence inside your Activity. If it is, return immediately, because there is nothing to do; if it isn't, then simply construct the Fragment and add it to the Activity like you normally would. Performing this check prevents your Fragment from needlessly being created again, which is what causes should check whether the Fragment is already in existence inside your Activity. If it is, return immediately, because there is nothing to do; if it isn't, then simply construct the Fragment and add it to the Activity like you normally would. Performing this check prevents your Fragment from needlessly being created again, which is what causes should check whether the Fragment is already in existence inside your Activity. If it is, return immediately, because there is nothing to do; if it isn't, then simply construct the Fragment and add it to the Activity like you normally would. Performing this check prevents your Fragment from needlessly being created again, which is what causes ненужное создание should check whether the Fragment is already in existence inside your Activity. If it is, return immediately, because there is nothing to do; if it isn't, then simply construct the Fragment and add it to the Activity like you normally would. Performing this check prevents your Fragment from needlessly being created again, which is what causes вызов onCreateView () `дважды!

См. Мою реализацию onNavigationItemSelected() ниже.

 public class MyActivity extends FragmentActivity implements ActionBar.OnNavigationListener { private static final String STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item"; private boolean mIsUserInitiatedNavItemSelection; // ... constructor code, etc. @Override public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState.containsKey(STATE_SELECTED_NAVIGATION_ITEM)) { getActionBar().setSelectedNavigationItem(savedInstanceState.getInt(STATE_SELECTED_NAVIGATION_ITEM)); } } @Override public void onSaveInstanceState(Bundle outState) { outState.putInt(STATE_SELECTED_NAVIGATION_ITEM, getActionBar().getSelectedNavigationIndex()); super.onSaveInstanceState(outState); } @Override public boolean onNavigationItemSelected(int position, long id) { Fragment fragment; switch (position) { // ... choose and construct fragment here } // is this the automatic (non-user initiated) call to onNavigationItemSelected() // that occurs when the activity is created/re-created? if (!mIsUserInitiatedNavItemSelection) { // all subsequent calls to onNavigationItemSelected() won't be automatic mIsUserInitiatedNavItemSelection = true; // has the same fragment already replaced the container and assumed its id? Fragment existingFragment = getSupportFragmentManager().findFragmentById(R.id.container); if (existingFragment != null && existingFragment.getClass().equals(fragment.getClass())) { return true; //nothing to do, because the fragment is already there } } getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit(); return true; } } 

Я заимствовал вдохновение для этого решения отсюда .

У меня была та же проблема с простой Activity, в которой был только один фрагмент (который иногда заменяется). Затем я понял, что я использую onSaveInstanceState только в фрагменте (и onCreateView для проверки savedInstanceState), а не в действии.

На устройстве активируется действие, содержащее фрагменты, и onCreated. Там я приложил необходимый фрагмент (что верно при первом запуске).

На устройстве Android сначала заново создал фрагмент, который был виден, а затем вызвал onCreate из содержащего действия, в котором был прикреплен мой фрагмент, заменив, таким образом, оригинальный видимый.

Чтобы этого не произошло, я просто изменил свою активность, чтобы проверить на savedInstanceState:

 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); If (savedInstanceState != null) return; // following code to attach fragment initially 

Я даже не перезаписывал onSaveInstanceState активности.

Мне кажется, что это потому, что вы каждый раз создаете свой TabListener … поэтому система воссоздает ваш фрагмент из файла savedInstanceState, а затем вы делаете это снова в своем onCreate.

Вы должны обернуть это в if(savedInstanceState == null) чтобы он срабатывал только в том случае, если нет сохраненногоInstanceState.