Intereting Posts
Как создать стиль приложения Twitter. Быстрые действия на Android Почему приложение умирает случайным образом? Как запустить код в потоке пользовательского интерфейса в тесте Android JUnit без Activity? Умение по электронной почте Android и тело сообщения Как программно устанавливать / редактировать содержимое веб-просмотра Распространение библиотек с открытым исходным кодом в приложениях Android Нормально ли для метода activity.onCreate () вызывать несколько раз Сбой сборки при запуске-андроиде из-за зависимостей В proguard, как сохранить набор имен методов классов? Задержка в потоковом аудио Android Studio – «Unmappable character для кодирования UTF-8» Файлы Java не найдены, которые расширяют CordovaActivity. При использовании "кордовы строить" Android-эмулятор: не удалось выбрать выделение Android: как конвертировать весь ImageView в Bitmap? Как показать MediaController во время воспроизведения звука в Android?

Дублированный идентификатор, тег null или родительский идентификатор с другим фрагментом для com.google.android.gms.maps.MapFragment

У меня есть приложение с тремя вкладками.

Каждая вкладка имеет свой собственный .xml-файл. Основной.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 

Solutions Collecting From Web of "Дублированный идентификатор, тег null или родительский идентификатор с другим фрагментом для com.google.android.gms.maps.MapFragment"

Ответ Мэтт предлагает работы, но это заставляет карту воссоздавать и перерисовывать, что не всегда желательно. После многих проб и ошибок я нашел решение, которое работает для меня:

 private static View view; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (view != null) { ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null) parent.removeView(view); } try { view = inflater.inflate(R.layout.map, container, false); } catch (InflateException e) { /* map is already there, just return view as it is */ } return view; } 

Для хорошей меры, вот «map.xml» (R.layout.map) с R.id.mapFragment (android: id = "@ + id / mapFragment"):

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mapLayout" android:layout_width="match_parent" android:layout_height="match_parent" > <fragment xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mapFragment" android:layout_width="match_parent" android:layout_height="match_parent" class="com.google.android.gms.maps.SupportMapFragment" /> </LinearLayout> 

Надеюсь, это поможет, но я не могу гарантировать, что он не будет иметь никаких побочных эффектов.

Изменить: Были некоторые неблагоприятные эффекты, например, при выходе из приложения и повторном запуске. Поскольку приложение не обязательно полностью отключается (но просто укладывается в фоновом режиме), предыдущий код, который я отправил, не смог перезапустить приложение. Я обновил код до того, что работает для меня, как входить и выходить из карты, так и выходить и перезапускать приложение, я не слишком доволен битком try-catch, но, похоже, он работает достаточно хорошо. При просмотре трассировки стека мне пришло в голову, что я могу просто проверить, находится ли фрагмент карты в FragmentManager, нет необходимости в блоке try-catch, обновлен код.

Дополнительные изменения: Оказывается, вам нужен этот try-catch в конце концов. Просто проверка фрагмента карты оказалась не очень хорошей. Blergh.

Проблема в том, что то, что вы пытаетесь сделать, не должно быть сделано. Вы не должны раздувать фрагменты внутри других фрагментов. Из документации Android:

Примечание. Вы не можете раздувать макет во фрагмент, если этот макет включает <фрагмент>. Вложенные фрагменты поддерживаются только при динамическом добавлении к фрагменту.

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

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

Просто измените свой XML-макет в пустой контейнер (при необходимости добавьте идентификатор):

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mapFragmentContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > </LinearLayout> 

Затем в режиме Fragment onViewCreated(View view, @Nullable Bundle savedInstanceState) :

 @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); FragmentManager fm = getChildFragmentManager(); SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentByTag("mapFragment"); if (mapFragment == null) { mapFragment = new SupportMapFragment(); FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.mapFragmentContainer, mapFragment, "mapFragment"); ft.commit(); fm.executePendingTransactions(); } mapFragment.getMapAsync(callback); } 

У меня была такая же проблема, и я смог ее разрешить, вручную удалив MapFragment в onDestroy() класса Fragment . Вот код, который работает и ссылается на MapFragment по ID в XML:

 @Override public void onDestroyView() { super.onDestroyView(); MapFragment f = (MapFragment) getFragmentManager() .findFragmentById(R.id.map); if (f != null) getFragmentManager().beginTransaction().remove(f).commit(); } 

Если вы не удалите MapFragment вручную, он будет зависать, чтобы не MapFragment много ресурсов для повторного создания / отображения отображения карты. Похоже, что сохранение базового MapView отлично подходит для переключения между вкладками, но при использовании в фрагментах это поведение заставляет дублировать MapView для каждого нового MapFragment с тем же идентификатором. Решение состоит в том, чтобы вручную удалить MapFragment и таким образом воссоздать базовую карту каждый раз, когда фрагмент завышен.

Я также отметил это в другом ответе [ 1 ].

Это мой ответ:

1, Создайте макет xml, как показано ниже:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/map_container" android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout> 

2, в классе Fragment, программно добавьте карту google.

 import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.SupportMapFragment; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * A simple {@link android.support.v4.app.Fragment} subclass. Activities that * contain this fragment must implement the * {@link MapFragment.OnFragmentInteractionListener} interface to handle * interaction events. Use the {@link MapFragment#newInstance} factory method to * create an instance of this fragment. * */ public class MapFragment extends Fragment { // TODO: Rename parameter arguments, choose names that match private GoogleMap mMap; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_map, container, false); SupportMapFragment mMapFragment = SupportMapFragment.newInstance(); mMap = mMapFragment.getMap(); FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); transaction.add(R.id.map_container, mMapFragment).commit(); return view; } @Override public void onAttach(Activity activity) { super.onAttach(activity); Log.d("Attach", "on attach"); } @Override public void onDetach() { super.onDetach(); } } 
  1. Как упоминалось @Justin Breitfeller, решение @Vidar Wahlberg – это взлом, который может не работать в будущей версии Android.
  2. @Vidar Wahlberg перехватывает взломать, потому что другое решение может «привести к тому, что карта будет воссоздана и перерисована, что не всегда желательно». Перенастройка карты может быть предотвращена путем сохранения старого фрагмента карты, а не создания нового экземпляра каждый раз.
  3. Решение @Matt не работает для меня (IllegalStateException)
  4. Как цитирует @Justin Breitfeller: «Вы не можете раздувать макет во фрагмент, когда этот макет включает в себя. Вложенные фрагменты поддерживаются только при добавлении к фрагменту динамически».

Мое решение:

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_map_list, container, false); // init //mapFragment = (SupportMapFragment)getChildFragmentManager().findFragmentById(R.id.map); // don't recreate fragment everytime ensure last map location/state are maintain if (mapFragment == null) { mapFragment = SupportMapFragment.newInstance(); mapFragment.getMapAsync(this); } FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); // R.id.map is a layout transaction.replace(R.id.map, mapFragment).commit(); return view; } 

Я бы рекомендовал replace() вместо attach() / detach() в вашей обработке вкладок.

Или переключитесь на ViewPager . Вот пример проекта, показывающий ViewPager , с вкладками, размещение 10 карт.

Для тех, кто все еще сталкивается с этой проблемой, лучший способ убедиться, что вы не получите эту ошибку с помощью карты в SupportMapFragment – это сделать Fragment расширением SupportMapFragment вместо того, чтобы SupportMapFragment внутри Фрагмента, используемого для Tab.

Я просто получил эту работу, используя ViewPager с FragmentPagerAdapter , с поддержкой SupportMapFragment в третьей вкладке.

Вот общая структура, обратите внимание, что нет необходимости переопределять метод onCreateView() , и нет необходимости раздувать любой макет xml:

 public class MapTabFragment extends SupportMapFragment implements OnMapReadyCallback { private GoogleMap mMap; private Marker marker; public MapTabFragment() { } @Override public void onResume() { super.onResume(); setUpMapIfNeeded(); } private void setUpMapIfNeeded() { if (mMap == null) { getMapAsync(this); } } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; setUpMap(); } private void setUpMap() { mMap.setMyLocationEnabled(true); mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); mMap.getUiSettings().setMapToolbarEnabled(false); mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng point) { //remove previously placed Marker if (marker != null) { marker.remove(); } //place marker where user just clicked marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker") .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA))); } }); } } 

Результат:

Введите описание изображения здесь

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

 public class MainActivity extends AppCompatActivity implements ActionBar.TabListener{ SectionsPagerAdapter mSectionsPagerAdapter; ViewPager mViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mSectionsPagerAdapter); final ActionBar actionBar = getSupportActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { actionBar.setSelectedNavigationItem(position); } }); for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this)); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { mViewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { } @Override public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { } public class SectionsPagerAdapter extends FragmentPagerAdapter { public SectionsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return PlaceholderFragment.newInstance(position + 1); case 1: return PlaceholderFragment.newInstance(position + 1); case 2: return MapTabFragment.newInstance(position + 1); } return null; } @Override public int getCount() { // Show 3 total pages. return 3; } @Override public CharSequence getPageTitle(int position) { Locale l = Locale.getDefault(); switch (position) { case 0: return getString(R.string.title_section1).toUpperCase(l); case 1: return getString(R.string.title_section2).toUpperCase(l); case 2: return getString(R.string.title_section3).toUpperCase(l); } return null; } } public static class PlaceholderFragment extends Fragment { private static final String ARG_SECTION_NUMBER = "section_number"; TextView text; public static PlaceholderFragment newInstance(int sectionNumber) { PlaceholderFragment fragment = new PlaceholderFragment(); Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } public PlaceholderFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); text = (TextView) rootView.findViewById(R.id.section_label); text.setText("placeholder"); return rootView; } } public static class MapTabFragment extends SupportMapFragment implements OnMapReadyCallback { private static final String ARG_SECTION_NUMBER = "section_number"; private GoogleMap mMap; private Marker marker; public static MapTabFragment newInstance(int sectionNumber) { MapTabFragment fragment = new MapTabFragment(); Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } public MapTabFragment() { } @Override public void onResume() { super.onResume(); Log.d("MyMap", "onResume"); setUpMapIfNeeded(); } private void setUpMapIfNeeded() { if (mMap == null) { Log.d("MyMap", "setUpMapIfNeeded"); getMapAsync(this); } } @Override public void onMapReady(GoogleMap googleMap) { Log.d("MyMap", "onMapReady"); mMap = googleMap; setUpMap(); } private void setUpMap() { mMap.setMyLocationEnabled(true); mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); mMap.getUiSettings().setMapToolbarEnabled(false); mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng point) { Log.d("MyMap", "MapClick"); //remove previously placed Marker if (marker != null) { marker.remove(); } //place marker where user just clicked marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker") .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA))); Log.d("MyMap", "MapClick After Add Marker"); } }); } } } 

Другое решение:

 if (view == null) { view = inflater.inflate(R.layout.nearbyplaces, container, false); } 

Вот и все, если не null, вам не нужно повторно инициализировать его удаление из родителя, это необязательный шаг.

Я потерял сегодня много часов, чтобы найти причину, к счастью, эта проблема связана не с реализацией MapFragment, поскольку это не работает, потому что вложенные фрагменты поддерживаются только через библиотеку поддержки от версии 11.

Моя реализация имеет активность с панелью действий (в режиме с вкладками) с двумя вкладками (без просмотра), одна имеет карту, а другая имеет список записей. Конечно, я был очень наивным, чтобы использовать MapFragment внутри своих вкладок-фрагментов, и приложение отключилось каждый раз, когда я переключился обратно на карту-вкладку.

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

Один из вариантов – использовать MapView (вместо MapFragment) с некоторыми накладными расходами (см. MapView Docs в качестве замены в layout.xml, другой вариант – использовать библиотеку поддержки от версии 11, а затем выполнить программный подход Поскольку вложенные фрагменты не поддерживаются с помощью макета или просто работают с программным путем, явно разрушая фрагмент (как в ответе от Matt / Vidar), btw: тот же эффект достигается с помощью MapView (вариант 1).

Но на самом деле я не хотел потерять карту каждый раз, когда я убираю ее, то есть хочу сохранить ее в памяти и очистить только при закрытии операции, поэтому я решил просто скрыть / показать карту во время табуляции, см. FragmentTransaction / hide

Я уважаю все ответы, но я нашел это одно решение для линейки: Если n – количество вкладок, то:

  mViewPager.setOffscreenPageLimit(n); 

Пример: В случае упоминания:

  mViewPager.setOffscreenPageLimit(2); 

View pager реализует очередь, поэтому вам не нужно удалять этот фрагмент. OnCreateView вызывается только один раз.

Объявить объект SupportMapFragment глобально

  private SupportMapFragment mapFragment; 

В методе onCreateView () помещается ниже кода

 mapFragment = (SupportMapFragment) getChildFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); 

В onDestroyView () помещается код ниже

 @Override public void onDestroyView() { super.onDestroyView(); if (mapFragment != null) getFragmentManager().beginTransaction().remove(mapFragment).commit(); } 

В вашем XML-файле, помещенном ниже кода

  <fragment android:id="@+id/map" android:name="com.abc.Driver.fragment.FragmentHome" class="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> 

Выше код решил мою проблему, и он отлично работает

Вложенные фрагменты в настоящее время не поддерживаются. Попробуйте пакет поддержки, версия 11 .

Вы пытались ссылаться на свой собственный класс MapFragment в файле макета?

 <?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.nfc.demo.MapFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> 

Если вы будете использовать только ответ Видара Уолберга, вы получите сообщение об ошибке, когда вы откроете другую деятельность (например) и вернитесь к карте. Или в моем случае откройте другую активность, а затем снова откройте новую карту (без использования кнопки возврата). Но когда вы сочетаете решение Vidar Wahlberg и решение Matt, у вас не будет исключений.

расположение

 <com.example.ui.layout.MapWrapperLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/map_relative_layout"> <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/root"> <fragment xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" class="com.google.android.gms.maps.SupportMapFragment" /> </RelativeLayout> </<com.example.ui.layout.MapWrapperLayout> 

Фрагмент

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHasOptionsMenu(true); if (view != null) { ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null){ parent.removeView(view); } } try { view = inflater.inflate(R.layout.map_view, null); if(view!=null){ ViewGroup root = (ViewGroup) view.findViewById(R.id.root); ... @Override public void onDestroyView() { super.onDestroyView(); Fragment fragment = this.getSherlockActivity().getSupportFragmentManager().findFragmentById(R.id.map); if (fragment != null) getFragmentManager().beginTransaction().remove(fragment).commit(); } 

У меня было это в viewPager, и авария была связана с тем, что у любого фрагмента должен быть свой собственный тег, дублирующие теги или идентификаторы для одного и того же фрагмента.

Я думаю, что были некоторые ошибки в предыдущей папке App-Compat lib для дочернего фрагмента. Я попробовал @ Vidar Wahlberg и @ Matt и они не работали для меня. После обновления библиотеки appcompat мой код работает отлично без каких-либо дополнительных усилий.

Здесь вы можете заметить, что ваше приложение сильно пострадает в любом из двух случаев:

1) Для повторного использования фрагмента с Картами снова фрагмент MapView должен быть удален, когда фрагмент, отображающий Карты, был заменен другим фрагментом в обратном вызове onDestroyView.

Else, когда вы пытаетесь раздуть тот же фрагмент дважды. Дублируйте идентификатор, тег null или родительский идентификатор с другим фрагментом для com.google.android.gms.maps.MapFragment .

2) Во-вторых, вы не должны смешивать операции app.Fragment с операциями API android.support.v4.app.Fragment, например, не использовать android.app.FragmentTransaction для удаления v4.app.Fragment type MapView Fragment. Смешение этого снова приведет к сбою со стороны фрагмента.

Вот пример фрагмента кода для правильного использования MapView

 import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMap.OnMapClickListener; import com.google.android.gms.maps.MapFragment; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MarkerOptions; import com.serveroverload.yago.R; /** * @author 663918 * */ public class HomeFragment extends Fragment implements LocationListener { // Class to do operations on the Map GoogleMap googleMap; private LocationManager locationManager; public static Fragment newInstance() { return new HomeFragment(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.home_fragment, container, false); Bundle bdl = getArguments(); // setuping locatiomanager to perfrom location related operations locationManager = (LocationManager) getActivity().getSystemService( Context.LOCATION_SERVICE); // Requesting locationmanager for location updates locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, 1, 1, this); // To get map from MapFragment from layout googleMap = ((MapFragment) getActivity().getFragmentManager() .findFragmentById(R.id.map)).getMap(); // To change the map type to Satellite // googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); // To show our current location in the map with dot // googleMap.setMyLocationEnabled(true); // To listen action whenever we click on the map googleMap.setOnMapClickListener(new OnMapClickListener() { @Override public void onMapClick(LatLng latLng) { /* * LatLng:Class will give us selected position lattigude and * longitude values */ Toast.makeText(getActivity(), latLng.toString(), Toast.LENGTH_LONG).show(); } }); changeMapMode(2); // googleMap.setSatellite(true); googleMap.setTrafficEnabled(true); googleMap.setBuildingsEnabled(true); googleMap.setMyLocationEnabled(true); return v; } private void doZoom() { if (googleMap != null) { googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(18.520430, 73.856744), 17)); } } private void changeMapMode(int mapMode) { if (googleMap != null) { switch (mapMode) { case 0: googleMap.setMapType(GoogleMap.MAP_TYPE_NONE); break; case 1: googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); break; case 2: googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); break; case 3: googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN); break; case 4: googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); break; default: break; } } } private void createMarker(double latitude, double longitude) { // double latitude = 17.385044; // double longitude = 78.486671; // lets place some 10 random markers for (int i = 0; i < 10; i++) { // random latitude and logitude double[] randomLocation = createRandLocation(latitude, longitude); // Adding a marker MarkerOptions marker = new MarkerOptions().position( new LatLng(randomLocation[0], randomLocation[1])).title( "Hello Maps " + i); Log.e("Random", "> " + randomLocation[0] + ", " + randomLocation[1]); // changing marker color if (i == 0) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_AZURE)); if (i == 1) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_BLUE)); if (i == 2) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_CYAN)); if (i == 3) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_GREEN)); if (i == 4) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA)); if (i == 5) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_ORANGE)); if (i == 6) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_RED)); if (i == 7) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_ROSE)); if (i == 8) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_VIOLET)); if (i == 9) marker.icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_YELLOW)); googleMap.addMarker(marker); // Move the camera to last position with a zoom level if (i == 9) { CameraPosition cameraPosition = new CameraPosition.Builder() .target(new LatLng(randomLocation[0], randomLocation[1])) .zoom(15).build(); googleMap.animateCamera(CameraUpdateFactory .newCameraPosition(cameraPosition)); } } } /* * creating random postion around a location for testing purpose only */ private double[] createRandLocation(double latitude, double longitude) { return new double[] { latitude + ((Math.random() - 0.5) / 500), longitude + ((Math.random() - 0.5) / 500), 150 + ((Math.random() - 0.5) * 10) }; } @Override public void onLocationChanged(Location location) { if (null != googleMap) { // To get lattitude value from location object double latti = location.getLatitude(); // To get longitude value from location object double longi = location.getLongitude(); // To hold lattitude and longitude values LatLng position = new LatLng(latti, longi); createMarker(latti, longi); // Creating object to pass our current location to the map MarkerOptions markerOptions = new MarkerOptions(); // To store current location in the markeroptions object markerOptions.position(position); // Zooming to our current location with zoom level 17.0f googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(position, 17f)); // adding markeroptions class object to the map to show our current // location in the map with help of default marker googleMap.addMarker(markerOptions); } } @Override public void onStatusChanged(String provider, int status, Bundle extras) { // TODO Auto-generated method stub } @Override public void onProviderEnabled(String provider) { // TODO Auto-generated method stub } @Override public void onProviderDisabled(String provider) { // TODO Auto-generated method stub } @Override public void onDestroyView() { // TODO Auto-generated method stub super.onDestroyView(); locationManager.removeUpdates(this); android.app.Fragment fragment = getActivity().getFragmentManager() .findFragmentById(R.id.map); if (null != fragment) { android.app.FragmentTransaction ft = getActivity() .getFragmentManager().beginTransaction(); ft.remove(fragment); ft.commit(); } } } 

XML

  <fragment android:id="@+id/map" android:name="com.google.android.gms.maps.MapFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> 

Результат выглядит так: Введите описание изображения здесь

Надеюсь, это поможет кому-то.

В этом решении вам не нужно принимать статическую переменную;

 Button nextBtn; private SupportMapFragment mMapFragment; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); if (mRootView != null) { ViewGroup parent = (ViewGroup) mRootView.getParent(); Utility.log(0,"removeView","mRootView not NULL"); if (parent != null) { Utility.log(0, "removeView", "view removeViewed"); parent.removeAllViews(); } } else { try { mRootView = inflater.inflate(R.layout.dummy_fragment_layout_one, container, false);// } catch (InflateException e) { /* map is already there, just return view as it is */ e.printStackTrace(); } } return mRootView; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); FragmentManager fm = getChildFragmentManager(); SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.mapView); if (mapFragment == null) { mapFragment = new SupportMapFragment(); FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.mapView, mapFragment, "mapFragment"); ft.commit(); fm.executePendingTransactions(); } //mapFragment.getMapAsync(this); nextBtn = (Button) view.findViewById(R.id.nextBtn); nextBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Utility.replaceSupportFragment(getActivity(),R.id.dummyFragment,dummyFragment_2.class.getSimpleName(),null,new dummyFragment_2()); } }); }` 
 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <com.google.android.gms.maps.MapView android:id="@+id/mapview" android:layout_width="100dip" android:layout_height="100dip" android:layout_alignParentTop="true" android:layout_alignRight="@+id/textView1" android:layout_marginRight="15dp" > </com.google.android.gms.maps.MapView> 

Почему вы не вставляете карту, используя объект MapView вместо MapFragment? Я не уверен, есть ли какие-либо ограничения в MapView, хотя я нашел его полезным.