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

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

Каждая вкладка имеет свой собственный .xml-файл. Основной.xml имеет свой собственный fragment карты. Это тот, который появляется, когда приложение запускается впервые.

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

Что здесь может быть не так?

Это мой основной class и мой main.xml, а также соответствующий class, который я использую (вы также найдете журнал ошибок внизу)

основной class

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(this, "map", MapFragment.class))); bar.addTab(bar .newTab() .setText("Settings") .setTabListener( new TabListener(this, "settings", SettingsFragment.class))); bar.addTab(bar .newTab() .setText("About") .setTabListener( new TabListener(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 implements ActionBar.TabListener { private final Activity mActivity; private final String mTag; private final Class mClass; private final Bundle mArgs; private Fragment mFragment; public TabListener(Activity activity, String tag, Class clz) { this(activity, tag, clz, null); } public TabListener(Activity activity, String tag, Class 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

     

соответствующий class (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 

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

 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”):

     

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

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

Дополнительные изменения: оказывается, что вам нужно, чтобы попробовать. Просто проверка fragmentа карты оказалась не очень хорошей. Blergh.

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

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

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

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

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

    

Затем в 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() classа 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 отлично подходит для переключения между вкладками, но при использовании в fragmentах это поведение вызывает создание дублирующего MapView на каждом новом MapFragment с тем же идентификатором. Решение состоит в том, чтобы вручную удалить MapFragment и таким образом воссоздать базовую карту каждый раз, когда fragment завышен.

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

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

1, создайте макет xml следующим образом:

   

2, в classе «Фрагмент», программно добавьте карту 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 перехватывает взлом, потому что другое решение может «заново создать и перерисовать карту, что не всегда желательно». Перенастройка карты может быть предотвращена путем сохранения старого fragmentа карты, а не создания нового экземпляра каждый раз.
  3. Решение @Matt не работает для меня (IllegalStateException)
  4. Как цитирует @Justin Breitfeller: «Вы не можете раздувать макет во fragment, когда этот макет включает в себя. Вложенные fragmentы поддерживаются только при динамическом добавлении fragmentа».

Мое решение:

 @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; } 

Объявить объект 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-файле введите ниже код

   

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

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

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

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

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

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

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

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

Для тех, кто все еще сталкивается с этой проблемой, лучший способ убедиться, что вы не получите эту ошибку с помощью карты в SupportMapFragment – это сделать Fragment расширением SupportMapFragment вместо того, чтобы SupportMapFragment внутри fragmentа, используемого для 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))); } }); } } 

Результат:

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

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

 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, вам не нужно повторно инициализировать его удаление из родителя, это необязательный шаг.

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

  mViewPager.setOffscreenPageLimit(n); 

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

  mViewPager.setOffscreenPageLimit(2); 

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

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

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

     

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

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

      

Фрагмент

 @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, и авария была вызвана тем, что у любого fragmentа должен быть свой собственный тег, дублирующие tags или идентификаторы для одного и того же fragmentа.

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

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

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

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

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

Вот пример 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

   

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

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

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

 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()); } }); }` 

I am a bit late to the party but Non of these answer helped me in my case. I was using Google map as SupportMapFragment and PlaceAutocompleteFragment both in my fragment. As all the answers pointed to the fact that the problem is with SupportMapFragment being the map to be recreated and redrawn.But after digging found out my problem was actually with PlaceAutocompleteFragment

So here is the working solution for those who are facing this problem because of SupportMapFragment and SupportMapFragment

  //Global SupportMapFragment mapFragment; mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.mapFragment); FragmentManager fm = getChildFragmentManager(); if (mapFragment == null) { mapFragment = SupportMapFragment.newInstance(); fm.beginTransaction().replace(R.id.mapFragment, mapFragment).commit(); fm.executePendingTransactions(); } mapFragment.getMapAsync(this); //Global PlaceAutocompleteFragment autocompleteFragment; if (autocompleteFragment == null) { autocompleteFragment = (PlaceAutocompleteFragment) getActivity().getFragmentManager().findFragmentById(R.id.place_autoCompleteFragment); } 

And in onDestroyView clear the SupportMapFragment and SupportMapFragment

 @Override public void onDestroyView() { super.onDestroyView(); if (getActivity() != null) { Log.e("res","place dlted"); android.app.FragmentManager fragmentManager = getActivity().getFragmentManager(); android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.remove(autocompleteFragment); fragmentTransaction.commit(); //Use commitAllowingStateLoss() if getting exception autocompleteFragment = null; } } 
     

Why don’t you insert a map using the MapView object instead of MapFragment ? I am not sure if there is any limitation in MapView,though i found it helpful.

Давайте будем гением компьютера.