У меня есть приложение с тремя вкладками.
Каждая вкладка имеет свой собственный .xml файл макета. Файл main.xml имеет собственный фрагмент карты. Это тот, который появляется при первом запуске приложения.
Все работает отлично, за исключением случаев, когда я переключаюсь между вкладками. Если я пытаюсь вернуться на вкладку фрагмента карты, я получаю эту ошибку. Переключение на другие вкладки и между ними работает просто отлично.
Что здесь может быть не так?
Это мой основной класс и мой main.xml, а также соответствующий класс, который я использую (вы также найдете журнал ошибок внизу)
основной класс
package com.nfc.demo;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Toast;
public class NFCDemoActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar
.newTab()
.setText("Map")
.setTabListener(
new TabListener<MapFragment>(this, "map",
MapFragment.class)));
bar.addTab(bar
.newTab()
.setText("Settings")
.setTabListener(
new TabListener<SettingsFragment>(this, "settings",
SettingsFragment.class)));
bar.addTab(bar
.newTab()
.setText("About")
.setTabListener(
new TabListener<AboutFragment>(this, "about",
AboutFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
// setContentView(R.layout.main);
}
@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()) {
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);
ft.add(android.R.id.content, mFragment, mTag);
} else {
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT)
.show();
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragment"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
соответствующий класс (MapFragment.java)
package com.nfc.demo;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.main, container, false);
}
public void onDestroy() {
super.onDestroy();
}
}
ошибка
android.view.InflateException: Binary XML file line #7:
Error inflating class fragment
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at com.nfc.demo.MapFragment.onCreateView(MapFragment.java:15)
at android.app.Fragment.performCreateView(Fragment.java:1695)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:885)
at android.app.FragmentManagerImpl.attachFragment(FragmentManager.java:1255)
at android.app.BackStackRecord.run(BackStackRecord.java:672)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:441)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5039)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException:
Binary XML file line #7: Duplicate id 0x7f040005, tag null, or
parent id 0xffffffff with another fragment for
com.google.android.gms.maps.MapFragment
at android.app.Activity.onCreateView(Activity.java:4722)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680)
... 19 more
Ответы:
Ответ, который Мэтт предлагает, работает, но заставляет карту пересоздавать и перерисовывать, что не всегда желательно. После многих проб и ошибок я нашел решение, которое работает для меня:
Для правильной оценки, вот "map.xml" (R.layout.map) с R.id.mapFragment (android: id = "@ + id / mapFragment"):
Я надеюсь, что это поможет, но я не могу гарантировать, что это не будет иметь никаких негативных последствий.
Изменить: Были некоторые негативные последствия, такие как выход из приложения и запуск его снова. Поскольку приложение не обязательно полностью закрыто (а просто переведено в спящий режим в фоновом режиме), предыдущий код, который я отправил, завершится неудачно при перезапуске приложения. Я обновил код так, чтобы он работал для меня, входя и выходя из карты, выходя и перезапуская приложение, я не слишком доволен битом try-catch, но, похоже, он работает достаточно хорошо.
Когда я смотрел на трассировку стека, мне пришло в голову, что я могу просто проверить, находится ли фрагмент карты в FragmentManager, нет необходимости в блоке try-catch, код обновлен.Больше правок: Оказывается, вам все-таки нужен этот try-catch. В конце концов, проверка фрагмента карты не сработала. Blergh.
источник
SupportMapFragment
inonDestroyView
.Проблема в том, что то, что вы пытаетесь сделать, не должно быть сделано. Вы не должны раздувать фрагменты внутри других фрагментов. Из документации Android :
Хотя вы можете выполнить задачу с помощью хаков, представленных здесь, я настоятельно рекомендую вам не делать этого. Невозможно быть уверенным, что эти хаки будут обрабатывать то, что делает каждая новая ОС Android, когда вы пытаетесь надуть макет для фрагмента, содержащего другой фрагмент.
Единственный поддерживаемый Android способ добавить фрагмент к другому фрагменту - через транзакцию из дочернего менеджера фрагментов.
Просто измените ваш XML-макет в пустой контейнер (добавьте идентификатор, если необходимо):
Затем в
onViewCreated(View view, @Nullable Bundle savedInstanceState)
методе Fragment :источник
4.3
при использованииSupportMapFragment
определенного в XML. Динамическое создание фрагмента и внедрение его в контейнерное представление решило проблему. Смотрите этот так ответ .SupportMapFragment.newInstance();
developers.google.com/maps/documentation/android-api/mapУ меня была такая же проблема и была в состоянии решить ее вручную удалением
MapFragment
вonDestroy()
способеFragment
класса. Вот код, который работает и ссылается наMapFragment
идентификатор в XML:Если вы не удалите
MapFragment
вручную, он будет зависать, так что это не потребует много ресурсов для повторного создания / отображения вида карты. Кажется, что сохранение базового уровняMapView
отлично подходит для переключения между вкладками, но при использовании во фрагментах такое поведение приводит к созданию дубликатаMapView
для каждого новогоMapFragment
с одинаковым идентификатором. Решение состоит в том, чтобы вручную удалитьMapFragment
и, таким образом, заново создавать базовую карту каждый раз, когда фрагмент надувается.Я также отметил это в другом ответе [ 1 ].
источник
Это мой ответ:
1, создать макет XML, как показано ниже:
2, в классе Fragment добавьте карту Google программным способом.
источник
mMapFragment.getMap();
возвращаетсяnull
. Есть идеи почему?Мое решение:
источник
Объявите объект SupportMapFragment глобально
В методе onCreateView () поместите код ниже
В onDestroyView () поместите код ниже
В вашем XML-файле поместите ниже код
Выше код решил мою проблему, и она работает нормально
источник
Я бы рекомендовал
replace()
вместоattach()
/detach()
в вашей вкладке обработки.Или переключитесь на
ViewPager
. Вот пример проекта показываяViewPager
, с закладками, хостинг 10 карт.источник
Другое решение:
Вот и все, если не NULL, вам не нужно повторно инициализировать его удаление из родителя является ненужным шагом.
источник
Я потерял часы сегодня, чтобы найти причину, к счастью, эта проблема не из-за реализации MapFragment, к сожалению, это не работает, потому что вложенные фрагменты поддерживаются только через библиотеку поддержки начиная с версии 11.
Моя реализация имеет действие с панелью действий (в режиме вкладок) с двумя вкладками (без окна просмотра), одна из которых имеет карту, а другая - список записей. Конечно, я был довольно наивен, чтобы использовать MapFragment внутри моих фрагментов вкладок, и вуаля приложение вылетало каждый раз, когда я переключался на map-tab.
(Та же проблема, что и у меня, в случае, если мой фрагмент табуляции будет раздувать любой макет, содержащий любой другой фрагмент).
Одним из вариантов является использование MapView (вместо MapFragment), хотя с некоторыми накладными расходами (см. Документы MapView как замену в файле layout.xml, другой вариант - использовать библиотеку поддержки, начиная с версии 11, но затем применять программный подход). так как вложенные фрагменты не поддерживаются ни макетом, ни просто программным путем обхода путем явного уничтожения фрагмента (как в ответе Мэтта / Видара), btw: тот же эффект достигается с помощью MapView (опция 1).
Но на самом деле я не хотел терять карту каждый раз, когда вкладывал, то есть я хотел хранить ее в памяти и очищать только после закрытия активности, поэтому я решил просто скрыть / показать карту во время вкладки, см. FragmentTransaction / hide
источник
Для тех, кто все еще сталкивается с этой проблемой, лучший способ удостовериться, что вы не получите эту ошибку с картой во вкладке, - это расширить фрагмент
SupportMapFragment
вместо того, чтобыSupportMapFragment
вкладывать фрагмент, используемый для вкладки.Я только что получил это, работая
ViewPager
с aFragmentPagerAdapter
, с SupportMapFragment в третьей вкладке.Вот общая структура, обратите внимание, что нет необходимости переопределять
onCreateView()
метод, и нет необходимости раздувать любой макет XML:Результат:
Вот полный код класса, который я использовал для тестирования, включая фрагмент-заполнитель, использованный для первых двух вкладок, и фрагмент карты, использованный для третьей вкладки:
источник
Я уважаю все ответы, но я нашел это одно линейное решение: если n - количество вкладок, то:
Пример: В случае, упомянутом:
View pager реализует очередь, поэтому вам не нужно позволять ему удалять этот фрагмент. onCreateView вызывается только один раз.
источник
Вы возвращаете или раздуваете макет дважды, просто проверьте, не раздули ли вы только один раз.
источник
Вложенные фрагменты в настоящее время не поддерживаются. Попробуйте пакет поддержки, редакция 11 .
источник
Вы пытались сослаться на свой пользовательский
MapFragment
класс в файле макета?источник
Если вы будете использовать только ответ Vidar Wahlberg, вы получите ошибку, когда откроете другую деятельность (например) и вернетесь к карте. Или, в моем случае, откройте другую активность, а затем из новой активности снова откройте карту (без использования кнопки назад). Но если вы объедините решение Vidar Wahlberg и решение Matt, у вас не будет исключений.
расположение
Фрагмент
источник
У меня это было в viewPager, и сбой был из-за того, что любой фрагмент должен иметь свой собственный тег, дублирующие теги или идентификаторы для одного и того же фрагмента не допускаются.
источник
Я думаю, что в предыдущей библиотеке App-Compat для дочернего фрагмента были некоторые ошибки. Я попробовал @ Vidar Wahlberg и @ Matt's, и они не работали для меня. После обновления библиотеки appcompat мой код работает без каких-либо дополнительных усилий.
источник
Обратите внимание на то, что ваше приложение будет плохо работать в любом из двух случаев:
Вот пример кода для правильного использования MapView
XML
Результат выглядит так: -
Надеюсь, это кому-нибудь поможет.
источник
В этом решении вам не нужно принимать статические переменные;
источник
Я немного опоздал на вечеринку, но ни один из этих ответов не помог мне в моем случае. Я использовал карту Google в качестве SupportMapFragment и PlaceAutocompleteFragment в моем фрагменте. Поскольку все ответы указывают на тот факт, что проблема в том, что SupportMapFragment является картой, которая должна быть воссоздана и перерисована. Но после копания выяснилось, что моя проблема была на самом деле с PlaceAutocompleteFragment
Итак, вот рабочее решение для тех, кто сталкивается с этой проблемой из-за SupportMapFragment и SupportMapFragment
И в onDestroyView очистите SupportMapFragment и SupportMapFragment
источник
Попробуйте установить идентификатор (android: id = "@ + id / maps_dialog") для родительского макета mapView. Работает для меня.
источник
Любой, кто приходит сюда сейчас и сталкивается с ошибками такого типа при открытии того
Dialog
или иногоFragment
с помощью Google АдресовAutocompleteSupportFragment
, попробуйте эту однострочную версию (я не знаю, насколько это безопасно, но это работает для меня):autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();
прежде чем уволить / уничтожить свой фрагмент.
источник
Почему бы вам не вставить карту, используя объект MapView вместо MapFragment? Я не уверен, есть ли какие-либо ограничения в MapView, хотя я нашел это полезным.
источник