binary xml file line #0 error when inflating the layout [duplicate] - android
I have an application with three tabs.
Each tab has its own layout .xml file. The main.xml has its own map fragment. It's the one that shows up when the application first launches.
Everything works fine except for when I change between tabs. If I try to switch back to the map fragment tab, I get this error. Switching to and between other tabs works just fine.
What could be wrong here?
This is my main class and my main.xml, as well as a relevant class that I use ( you will also find the error log at the bottom )
main 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<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>
relevant 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();
}
}
error
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
The answer Matt suggests works, but it causes the map to be recreated and redrawn, which isn't always desirable.
After lots of trial and error, I found a solution that works for me:
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;
}
For good measure, here's "map.xml" (R.layout.map) with 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>
I hope this helps, but I can't guarantee that it doesn't have any adverse effects.
Edit: There were some adverse effects, such as when exiting the application and starting it again. Since the application isn't necessarily completely shut down (but just put to sleep in the background), the previous code i submitted would fail upon restarting the application. I've updated the code to something that works for me, both going in & out of the map and exiting and restarting the application, I'm not too happy with the try-catch bit, but it seem to work well enough. When looking at the stack trace it occurred to me that I could just check if the map fragment is in the FragmentManager, no need for the try-catch block, code updated.
More edits: It turns out that you need that try-catch after all. Just checking for the map fragment turned out not to work so well after all. Blergh.
The problem is that what you are trying to do shouldn't be done. You shouldn't be inflating fragments inside other fragments. From Android's documentation:
Note: You cannot inflate a layout into a fragment when that layout
includes a <fragment>. Nested fragments are only supported when added
to a fragment dynamically.
While you may be able to accomplish the task with the hacks presented here, I highly suggest you don't do it. Its impossible to be sure that these hacks will handle what each new Android OS does when you try to inflate a layout for a fragment containing another fragment.
The only Android-supported way to add a fragment to another fragment is via a transaction from the child fragment manager.
Simply change your XML layout into an empty container (add an ID if needed):
<?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>
Then in the Fragment onViewCreated(View view, #Nullable Bundle savedInstanceState) method:
#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);
}
I had the same issue and was able to resolve it by manually removing the MapFragment in the onDestroy() method of the Fragment class. Here is code that works and references the MapFragment by ID in the XML:
#Override
public void onDestroyView() {
super.onDestroyView();
MapFragment f = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map);
if (f != null)
getFragmentManager().beginTransaction().remove(f).commit();
}
If you don't remove the MapFragment manually, it will hang around so that it doesn't cost a lot of resources to recreate/show the map view again. It seems that keeping the underlying MapView is great for switching back and forth between tabs, but when used in fragments this behavior causes a duplicate MapView to be created upon each new MapFragment with the same ID. The solution is to manually remove the MapFragment and thus recreate the underlying map each time the fragment is inflated.
I also noted this in another answer [1].
This is my answer:
1, Create a layout xml like following:
<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, in the Fragment class, add a google map programmatically.
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();
}
}
As mentioned by #Justin Breitfeller, #Vidar Wahlberg solution is a hack which might not work in future version of Android.
#Vidar Wahlberg perfer a hack because other solution might "cause the map to be recreated and redrawn, which isn't always desirable". Map redraw could be prevented by maintaining the old map fragment, rather than creating a new instance every time.
#Matt solution doesn't work for me (IllegalStateException)
As quoted by #Justin Breitfeller, "You cannot inflate a layout into a fragment when that layout includes a . Nested fragments are only supported when added to a fragment dynamically."
My solution:
#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;
}
Declare SupportMapFragment object globally
private SupportMapFragment mapFragment;
In onCreateView() method put below code
mapFragment = (SupportMapFragment) getChildFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
In onDestroyView() put below code
#Override
public void onDestroyView() {
super.onDestroyView();
if (mapFragment != null)
getFragmentManager().beginTransaction().remove(mapFragment).commit();
}
In your xml file put below code
<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"
/>
Above code solved my problem and it's working fine
I would recommend replace() rather than attach()/detach() in your tab handling.
Or, switch to ViewPager. Here is a sample project showing a ViewPager, with tabs, hosting 10 maps.
Another solution:
if (view == null) {
view = inflater.inflate(R.layout.nearbyplaces, container, false);
}
That's it, if not null you don't need to reinitialize it removing from parent is unnecessary step.
You are returning or inflating layout twice, just check to see if you only inflate once.
I have lost hours today to find the reason, fortunately this issue is not because of MapFragment implementation, fnfortunately, this does not work because nested fragments are only supported through support library from rev 11.
My implementation has an activity with actionbar (in tabbed mode) with two tabs (no viewpager), one having the map and the other having a list of entries. Of course I've been quite naive to use MapFragment inside my tab-fragments, et voila the app crashed everytime I switched back to map-tab.
( The same issue I also would have in case my tab-fragment would inflate any layout containing any other fragment ).
One option is to use the MapView (instead of MapFragment), with some overhead though ( see MapView Docs as drop-in replacement in the layout.xml, another option is to use support-library up from rev. 11 but then take programmatic approach since nested fragments are neither supported via layout. Or just working around programmatically by explicitely destroying the fragment (like in the answer from Matt / Vidar), btw: same effect is achieved using the MapView (option 1).
But actually, I did not want to loose the map everytime I tab away, that is, I wanted to keep it in memory and cleanup only upon activity close, so I decided to simply hide/show the map while tabbing, see FragmentTransaction / hide
For those who are still running into this issue, the best way to make sure you don't get this error with a Map in a Tab is to make the Fragment extend SupportMapFragment instead of nesting a SupportMapFragment inside the Fragment used for the Tab.
I just got this working using a ViewPager with a FragmentPagerAdapter, with the SupportMapFragment in the third Tab.
Here is the general structure, note there is no need to override the onCreateView() method, and there is no need to inflate any layout 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)));
}
});
}
}
Result:
Here is the full class code that I used to test with, which includes the placeholder Fragment used for the first two Tabs, and the Map Fragment used for the third Tab:
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");
}
});
}
}
}
I respect all the answers but i found this one liner solution:
If n Is the Number of tabs then:
mViewPager.setOffscreenPageLimit(n);
Example:
In case mentioned :
mViewPager.setOffscreenPageLimit(2);
View pager implements a queue so, you don't have to let it remove that fragment. onCreateView is called only once.
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;
}
}
Nested fragments are not currently supported.
Try Support Package, revision 11.
Have you been trying to reference your custom MapFragment class in the layout file?
<?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>
If you will use only Vidar Wahlberg answer, you get error when you open other activity (for example) and back to map. Or in my case open other activity and then from new activity open map again( without use back button).
But when you combine Vidar Wahlberg solution and Matt solution you will have not exceptions.
layout
<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>
Fragment
#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();
}
I had this in viewPager and the crash was because any fragment had to have its own tag, duplicate tags or ids for same fragment are not allowed.
I think there was some bugs in previous App-Compat lib for child Fragment. I tried #Vidar Wahlberg and #Matt's ans they did not work for me. After updating the appcompat library my code run perfectly without any extra effort.
Things to Note here is your app will crash badly in either of two cases:-
1) In order to reuse fragment with Maps again MapView Fragment must be removed when
your fragment showing Maps got replaced with other fragment in onDestroyView callback.
else when you try to inflate same fragment twice Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment error will happen.
2) Secondly you must not mix app.Fragment operations with
android.support.v4.app.Fragment API operations eg.do not use
android.app.FragmentTransaction to remove v4.app.Fragment type
MapView Fragment. Mixing this will again result into crash from fragment side.
Here is sample code snippet for correct usage of 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"
/>
Result looks like this :-
Hope it will help Somebody.
In this solution you do not need to take static variable;
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());
}
});
}`
Try setting an id (android:id="#+id/maps_dialog") for your mapView parent layout. Works for me.
Anyone coming here now that's getting this type of error when opening a Dialog or other Fragment with the Google Places AutocompleteSupportFragment, try this one-liner (I don't know how safe this is but it works for me):
autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();
before you dismiss/destroy your fragment.
<?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>
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.
Related
getMapAsync in Fragment
I am using Sliding Tabs with different fragments. In map fragment I cannot use Nested Fragment as XML. Below is Java code and XMLfile. I am stuck at getMapAsync() method So How Can I get Map using getMapAsync without Any exception? I will really appreciate your cooperation. Food Fragment Java: public class FoodFragment extends Fragment { private SupportMapFragment mapfragment; #Nullable #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view= inflater.inflate(R.layout.food_layout, null); mapfragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.maps_frame); if (mapfragment == null) { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); mapfragment = SupportMapFragment.newInstance(); fragmentTransaction.replace(R.id.maps_frame,mapfragment).commit(); } mapfragment.getMapAsync((OnMapReadyCallback) getActivity().getApplicationContext()); return view; } } Food XML file, <FrameLayout android:layout_width="match_parent" android:layout_height="2in" android:id="#+id/maps_frame" android:background="#color/colorAccent" android:layout_alignParentBottom="true"> </FrameLayout>
Your ApplicationContext cannot be casts as OnMapReadyCallback you can create new instance of OnMapReadyCallback() as an anonymous class instead like here is what you might be searching for mapfragment.getMapAsync(new OnMapReadyCallback() { #Override public void onMapReady(GoogleMap googleMap) { if (googleMap != null) { // your additional codes goes here ..... } } You can refer this example as well if you need.
SupportMapFragment null on SECOND load
There is long time I'm trying to solve this issue. I've read so much questions about similiar issues on stackoverflow and other sources that I'm unable to list all of them. My problem is different from the others I've read since the first time I try load the fragment with the map all works fine... Is on the second time I try to access the fragment with the map without restarting the app that it crashes with a null pointer on: googleMap = map.getMap(); because map is null: map = (SupportMapFragment) myContext.getSupportFragmentManager(). findFragmentById(R.id.map); The key differences are then: The first time it works fine The second time findFragmentById returns null, but no error I can't debug it because AVD emulator has no Google Play services installed. MainActivity has a NavigationDrawer, and depending on the option selected, a Fragment or another is selected. Here some of the code related to this issue: fragment_map.xml: <?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"> <fragment android:id="#+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> MyFragment.java: ... import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; ... public class MyFragment extends Fragment implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener { private GoogleMap googleMap; private View fragmentView; private static WorldFragment fragment; private FragmentActivity myContext; ... public static MyFragment getInstance() { if (fragment == null) { fragment = new MyFragment(); } return fragment; } #Override public void onAttach(Activity activity) { super.onAttach(activity); myContext = (FragmentActivity) activity; } #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); try { GlobalState gs = (GlobalState) getActivity().getApplication(); // recover View from previous use fragmentView = gs.getWorldFragmentView(); if (fragmentView == null) { fragmentView = inflater.inflate(R.layout.fragment_map, container, false); // save View for next use gs.setFragmentView(fragmentView); } else { ViewGroup parent = (ViewGroup) fragmentView.getParent(); parent.removeView(fragmentView); } } catch (InflateException e) { // map is already there, just return view as it is } return fragmentView; } ... #Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Set up the map fragment SupportMapFragment map = (SupportMapFragment) myContext. getSupportFragmentManager().findFragmentById(R.id.map); /* // If I active this code, no crash, but fragment remains // in gray background without map. // At least, if the app crashes, it restarts and the map // gets loaded the first time if (map == null) { Toast.makeText(getActivity(), getString(R.string.maps_not_supported), Toast.LENGTH_LONG).show(); return; } */ googleMap = map.getMap(); if (googleMap != null) { // Enable the current location "blue dot" googleMap.setMyLocationEnabled(true); ... } else { Toast.makeText(getActivity(), getString(R.string.maps_not_supported), Toast.LENGTH_LONG).show(); } } ... } MainActivity.java: public class MainActivity extends FragmentActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks { #Override public void onNavigationDrawerItemSelected(int position) { Fragment contentFragment; switch (selectedOption) { case MAP: contentFragment = MyFragment.getInstance(); break; case XXX: ... break; default: contentFragment = new OtherFragment(); } // update the main content by replacing fragments FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.container, contentFragment) .commit(); } } Stacktrace: java.lang.NullPointerException at xxx.xxxxx.xxx.fragments.MyFragment.onActivityCreated(MyFragment.java:193) at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1508) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:958) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1115) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1478) at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:446) at android.os.Handler.handleCallback(Handler.java:725) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:153) at android.app.ActivityThread.main(ActivityThread.java:5297) at java.lang.reflect.Method.invokeNative(Method.java) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600) at dalvik.system.NativeStart.main(NativeStart.java) Please, let me know if you need some more information. Thank you very much for your time and attention. 30/12/2014: source code of MainActivity's onCreate() method #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GlobalState applicationContext = (GlobalState) getApplicationContext(); applicationContext.updateLocale(); NavigationDrawerFragment.setItems(applicationContext.getMenuItems()); setContentView(R.layout.activity_main); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mTitle = getTitle(); // Set up the drawer. mNavigationDrawerFragment.setUp( R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout)); // Treating incoming notifications Intent intent = getIntent(); String action = intent.getAction(); if (action != null && !action.isEmpty()) { ItemType itemType = ItemType.valueOf(action); String title = applicationContext.getMenuItemByType(itemType).getTitle(); String itemId = null; try { JSONObject json = new JSONObject(intent.getExtras().getString("com.parse.Data")); itemId = json.getString(EXTRA_ITEM_ID); } catch (JSONException e) { Crashlytics.logException(e); } launchFragment(itemType, null, title, itemId); } }
You have to remove the fragment in the onDestroyView() method of your MyFragment class. Sample code to remove: #Override public void onDestroyView() { SupportMapFragment f = (SupportMapFragment)myContext.getSupportFragmentManager().findFragmentById(R.id.map); if (f != null) { try { myContext.getSupportFragmentManager().beginTransaction().remove(f).commit(); } catch (Exception e) { e.printStackTrace(); } } super.onDestroyView(); }
Android - SupportMapFragment error when opening for second time [duplicate]
When opening my SupportMapFragment (Android maps v2) for a second time (calling setContentView) I get the following error: 01-28 16:27:21.374: E/AndroidRuntime(32743): FATAL EXCEPTION: main 01-28 16:27:21.374: E/AndroidRuntime(32743): android.view.InflateException: Binary XML file line #6: Error inflating class fragment 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.view.LayoutInflater.rInflate(LayoutInflater.java:746) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.view.View.inflate(View.java:16119) 01-28 16:27:21.374: E/AndroidRuntime(32743): at mypackage.MyView.<init>(HitsView.java:26) 01-28 16:27:21.374: E/AndroidRuntime(32743): at mypackage.MenuListFragment.onItemClick(MenuListFragment.java:133) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.widget.AdapterView.performItemClick(AdapterView.java:298) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.widget.AbsListView.performItemClick(AbsListView.java:1086) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.widget.AbsListView$PerformClick.run(AbsListView.java:2855) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.widget.AbsListView$1.run(AbsListView.java:3529) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.os.Handler.handleCallback(Handler.java:615) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.os.Handler.dispatchMessage(Handler.java:92) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.os.Looper.loop(Looper.java:137) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.app.ActivityThread.main(ActivityThread.java:4745) 01-28 16:27:21.374: E/AndroidRuntime(32743): at java.lang.reflect.Method.invokeNative(Native Method) 01-28 16:27:21.374: E/AndroidRuntime(32743): at java.lang.reflect.Method.invoke(Method.java:511) 01-28 16:27:21.374: E/AndroidRuntime(32743): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 01-28 16:27:21.374: E/AndroidRuntime(32743): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 01-28 16:27:21.374: E/AndroidRuntime(32743): at dalvik.system.NativeStart.main(Native Method) 01-28 16:27:21.374: E/AndroidRuntime(32743): Caused by: java.lang.IllegalArgumentException: Binary XML file line #6: Duplicate id 0x7f04003b, tag null, or parent id 0x0 with another fragment for com.google.android.gms.maps.SupportMapFragment 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:285) 01-28 16:27:21.374: E/AndroidRuntime(32743): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:676) 01-28 16:27:21.374: E/AndroidRuntime(32743): ... 20 more The XML file: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:map="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" > <fragment android:id="#+id/hits_map" android:layout_width="match_parent" android:layout_height="wrap_content" class="com.google.android.gms.maps.SupportMapFragment" map:mapType="normal" map:uiZoomControls="false" map:uiZoomGestures="true" map:cameraZoom="13" map:uiRotateGestures="true" map:uiTiltGestures="true"/> </RelativeLayout> MyView.class: public class MyView extends RelativeLayout { private GoogleMap map; public MyView(Context context, FragmentActivity activity) { super(context); inflate(activity, R.layout.activity_hits, this); this.map = ((SupportMapFragment) activity.getSupportFragmentManager() .findFragmentById(R.id.hits_map)).getMap(); } } I have no idea what this error means. Can someone explain this?
Update: As an alternative solution (which I think is much better) you can use a MapView which is described: here I ran across a similar problem while working with a tabs implementation. With Google Maps V2, you are stuck with the SupportMapFragment, so using the MapView isn't an option. Between juanmeanwhile's post and comment #1 found here (https://code.google.com/p/gmaps-api-issues/issues/detail?id=5064#c1), I managed to figure out what I was doing wrong. So, for anyone else getting the Duplicate id error, make sure you declare the fragment programatically, not in XML. This probably means nesting layouts. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:map="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- Lots of fancy layout --> <RelativeLayout android:id="#+id/map" android:layout_width="match_parent" android:layout_height="wrap_content"> </RelativeLayout> </RelativeLayout> Then you need to create your fragment programatically, but it needs to be done carefully with consideration for the Fragments lifecycle (http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager()). To ensure everything is created at the right time, your code should look like this (taken from comment #1). public class MyFragment extends Fragment { private SupportMapFragment fragment; private GoogleMap map; #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.layout_with_map, container, false); } #Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); FragmentManager fm = getChildFragmentManager(); fragment = (SupportMapFragment) fm.findFragmentById(R.id.map); if (fragment == null) { fragment = SupportMapFragment.newInstance(); fm.beginTransaction().replace(R.id.map, fragment).commit(); } } #Override public void onResume() { super.onResume(); if (map == null) { map = fragment.getMap(); map.addMarker(new MarkerOptions().position(new LatLng(0, 0))); } } } Hopefully this saves some time.
Instead of declaring de SupportMapFragment in layout, do it programatically and be sure you use getChildFragmentMananger instead of the classic getFragmentManager() to create the fragment. mMapFragment = SupportMapFragment.newInstance(); FragmentTransaction fragmentTransaction = mMapFragment.getChildFragmentManager().beginTransaction(); fragmentTransaction.add(R.id.map_root, mMapFragment); fragmentTransaction.commit(); Keep this SupportMapFragment mMapFragment as you will need it to retrieve the GoogleMap object: GoogleMap map = mMapFragment.getMap();
I've spend a half day resolving this trouble and only found a workaround. Override your onCreate method in YourFragment class: public void onCreate(Bundle savedInstanceState) { setRetainInstance(true); super.onCreate(savedInstanceState); } And override your onDestroy method: #Override public void onDestroyView() { super.onDestroyView(); try { SupportMapFragment fragment = (SupportMapFragment) getActivity() .getSupportFragmentManager().findFragmentById( R.id.multi_inns_on_map); if (fragment != null) getFragmentManager().beginTransaction().remove(fragment).commit(); } catch (IllegalStateException e) { //handle this situation because you are necessary will get //an exception here :-( } }
I had passed all this day looking for the solution to this problem, reading all the workaroung here, and I found this way to solve the problems. I will post all the code, because this question lacks a complete answer, just some tiny snippet that does match a full scenario and does help anybody. public class LocationFragment extends Fragment implements View.OnClickListener { private GoogleMap googleMap; private static View rootView; private SupportMapFragment supportMapFragment; public LocationFragment() { } #Override public void onDestroyView() { super.onDestroyView(); } #Override public void onCreate(Bundle savedInstanceState) { setRetainInstance(true); super.onCreate(savedInstanceState); } #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if(savedInstanceState == null) { if (rootView != null) { ViewGroup parent = (ViewGroup) rootView.getParent(); if (parent != null) parent.removeView(rootView); } try{ if(rootView == null) { rootView = inflater.inflate(R.layout.fragment_location, container, false); } supportMapFragment = (SupportMapFragment) getFragmentManager().findFragmentById(R.id.map); LocationUtility locationUtility = LocationUtility.getInstance(context); GoogleMap googleMap = locationUtility.initilizeMap(supportMapFragment); //More code } catch (Exception e) { e.printStackTrace(); } } return rootView; } } By debugging this, you will notice that it does not matter how the fragment operates, you dont need botter for the onDestroyMethod, etc, since the validation of null, just operates with the current instance of the object. Enjoy! ;)
I ran into this issue and found in my android logs that my hardware acceleration was not turned on. After I turned it on in my AndroidManifest I was displaying the maps multiple times was no longer an issue. I was using an x86 emulator.
After lot of Hit and tries and I finally manage to use MapView Fragment perfactly in my App and result looks like this :- , here is my MapView Fragment Class :- 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; 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(3); // 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(); } } } My Xml file looks like this:- <?xml version="1.0" encoding="utf-8"?> <fragment xmlns:android="http://schemas.android.com/apk/res/android" android:id="#+id/map" android:name="com.google.android.gms.maps.MapFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> Most Important thing to note is that DO Not Mix app.Fragment with v4.Fragments else app will crash badly. As you can see I have used app.Fragment to attach and remove my MapView Fragment Hope it will help Somebody
I tried various solutions proposed here, and the best one IMHO is the MapView. Anyway, I found another solution that could suit your needs (or maybe not). I had my SupportMapFragment inside a ViewPager+FragmentPagerAdapter with 3 tabs. So I just configured the ViewPager in a way that it never destroys the tab where the SupportMapFragment is. viewPager.setOffScreenPageLimit(2); viewPager.setAdapter(...); With this call, the ViewPager will always maintain two pages after, and two pages before the current one, so none of my three tabs is never destroyed/recreated. Problem solved :) Note that memory consumption will increase
private MainActivity mainActObj; #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mainActObj = getMainActivity(); } #SuppressWarnings("unchecked") #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (mainActObj.mapView == null) { return mainActObj.mapView = inflater.inflate(R.layout.fragment_map, null); } else { ViewGroup parent = (ViewGroup) mainActObj.mapView.getParent(); if (parent != null) { parent.removeView(mainActObj.mapView); } try { return mainActObj.mapView = inflater.inflate( R.layout.fragment_map, container, false); } catch (InflateException except) { return mainActObj.mapView; } } }
try this code #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { finder = new LocationFinder(getActivity()); view = inflater.inflate(R.layout.fragment_secondfragement, container, false); initMap(); // getLocation(); return view; } public void initMap(){ if (map == null) { mapFragment = (SupportMapFragment) getChildFragmentManager() .findFragmentById(R.id.map); } map = mapFragment.getMap(); map.setMyLocationEnabled(true); map.getUiSettings().setMyLocationButtonEnabled(true); }
I have the same issue and solved by creating google map dynamically. What you have to do is making a class which is extending SupportMapFragment class. This methods works on gingerbread to lollipop and on nested fragments because it will create the map dynamically so don't have to worry about removing the fragment or anything like that. Step1: public class DynamicMapFragment extends SupportMapFragment { public GoogleMap googleMap; public View mOriginalContentView; public static int statusGooglePlayService; #Override public void onCreate(Bundle arg0) { super.onCreate(arg0); } #Override public View onCreateView(LayoutInflater mInflater, ViewGroup parent, Bundle arg2) { mOriginalContentView = super.onCreateView(mInflater, parent, arg2); //this is for getting height of the phone screen DisplayMetrics displaymetrics = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay() .getMetrics(displaymetrics); int height = displaymetrics.heightPixels; //Here you can define the height of the map as you desire FrameLayout.LayoutParams parentLayout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, height / 3); mOriginalContentView.setLayoutParams(parentLayout); return mOriginalContentView; } #Override public View getView() { return mOriginalContentView; } #Override public void onInflate(Activity arg0, AttributeSet arg1, Bundle arg2) { super.onInflate(arg0, arg1, arg2); } #Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); googleMap = getMap(); statusGooglePlayService = GooglePlayServicesUtil .isGooglePlayServicesAvailable(getActivity()); if (statusGooglePlayService == ConnectionResult.SUCCESS) { if (googleMap != null) { // do whatever you want to do with map } } else { Dialog dialog = GooglePlayServicesUtil.getErrorDialog( statusGooglePlayService, getActivity(), -1); dialog.show(); } } } Then call the above created class from Activity, Fragment like this: Step2: //Calling the method from a fragment private void createMap(View rootView) { FrameLayout mapLayout = (FrameLayout)rootView.findViewById(R.id.location_map); FragmentTransaction mTransaction = getActivity() .getSupportFragmentManager().beginTransaction(); SupportMapFragment mFRaFragment = new DynamicMapFragment(); mTransaction.add(mapLayout.getId(), mFRaFragment); mTransaction.commit(); try { MapsInitializer.initialize(activity); } catch (Exception e) { e.printStackTrace(); } } XML layout for fragment where you have to add Framelayout instead of a fragment for map Step3: <?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" > <FrameLayout android:id="#+id/location_map" android:layout_width="match_parent" android:layout_height="250dp" android:layout_weight="0" /> <!-- You can add any other view also whatever you want to add <Button android:layout_width="match_parent" android:layout_height=""wrap_content"" android:layout_weight="0" /> --> </LinearLayout>
just put on onCreateView if (view != null) { ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null) parent.removeView(view); } view = inflater.inflate(R.layout.map, container, false);
Google Maps API v2 Custom MapFragment + SimpleFragment
I need to create a simple application which contain 1. Custom MapFragment which can display current location (LocationListener?), two markers, etc. 2. Simple empty Fragments. The application must not use support v4 (sdk 11+) / FragmentActivity. Empty fragment. XML: <?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" android:background="#android:color/black" > </RelativeLayout> Class: public class FirstFragment extends Fragment{ public FirstFragment() { } #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.first_fragment, container, false); return view; } } Map fragment (try to make it singleton). XML: <?xml version="1.0" encoding="utf-8"?> <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.MapFragment"/> Class public class ThirdMapFragment extends MapFragment { public static MapFragment instance; #Override public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) { View view; view = arg0.inflate(R.layout.map_fragment, arg1, false); return view; } public static MapFragment getInstance() { if (instance == null) { instance = newInstance(); } // NullPointerException in getMap() ??? // instance.getMap().addMarker(new MarkerOptions().position(new LatLng(41.1, 39))); return instance; } #Override public void onCreate(Bundle arg0) { super.onCreate(arg0); } } Main activity: public class ActivityMain extends Activity implements OnClickListener { FragmentManager fragmentManager; FragmentTransaction transaction; FirstFragment firstFragment; MapFragment mapFragment; // Buttons Button b4; int i = 0; #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); fragmentManager = getFragmentManager(); transaction = fragmentManager.beginTransaction(); b4 = (Button) findViewById(R.id.button4); b1.setOnClickListener(this); firstFragment = new FirstFragment(); mapFragment = ThirdMapFragment.getInstance(); } #Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } #Override public void onClick(View v) { makeTransaction(v.getId()); } private void makeTransaction(int id) { i++; transaction = fragmentManager.beginTransaction(); if (i == 1) { transaction.replace(R.id.relativeLayout, firstFragment).commit(); } else if (i == 2) { transaction.replace(R.id.relativeLayout, secondFragment).commit(); } else { transaction.replace(R.id.relativeLayout, mapFragment).commit(); i = 0; } } } R.id.relativeLayout - Layout in main.xml But I do not see the map (empty screen with zoom buttons) and how to make LocationListener? Thx.
if you are using the support library you have to use the SupportMapFragment class and not the MapFragment for the map fragment. your activity should be a FragmentActivity for the support of the library. as well as for the Fragment manager. you have to getSupportFragmentManager(). Take a look at this code: import com.google.android.gms.common.GooglePlayServicesUtil; import android.os.Bundle; import com.google.android.gms.maps.*; public class Map extends android.support.v4.app.FragmentActivity { private GoogleMap map; #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.map); if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext()) == 0) { map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap(); map.setMapType(GoogleMap.MAP_TYPE_HYBRID); } }} If you still don't see a map after you made those changes. Check your API Console that you have turn on the Google Map ANDROID API V2 and not just the Google Map API V2 that could cause the same result as you describe.
the view is not created yet that's why getMap() returns null, map doesn't exist, put your code in onResume
Put in the manifest, the key for android? For example: <application android:icon="#drawable/android" android:label="#string/app_name" android:theme="#style/AppTheme" > <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyB2s6isdfsghDfgdfdfgDFGDG-yi4-aAYUma0Owc"/> https://developers.google.com/maps/documentation/android/start
Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment
I have an application with three tabs. Each tab has its own layout .xml file. The main.xml has its own map fragment. It's the one that shows up when the application first launches. Everything works fine except for when I change between tabs. If I try to switch back to the map fragment tab, I get this error. Switching to and between other tabs works just fine. What could be wrong here? This is my main class and my main.xml, as well as a relevant class that I use ( you will also find the error log at the bottom ) main 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<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> relevant 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(); } } error 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
The answer Matt suggests works, but it causes the map to be recreated and redrawn, which isn't always desirable. After lots of trial and error, I found a solution that works for me: 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; } For good measure, here's "map.xml" (R.layout.map) with 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> I hope this helps, but I can't guarantee that it doesn't have any adverse effects. Edit: There were some adverse effects, such as when exiting the application and starting it again. Since the application isn't necessarily completely shut down (but just put to sleep in the background), the previous code i submitted would fail upon restarting the application. I've updated the code to something that works for me, both going in & out of the map and exiting and restarting the application, I'm not too happy with the try-catch bit, but it seem to work well enough. When looking at the stack trace it occurred to me that I could just check if the map fragment is in the FragmentManager, no need for the try-catch block, code updated. More edits: It turns out that you need that try-catch after all. Just checking for the map fragment turned out not to work so well after all. Blergh.
The problem is that what you are trying to do shouldn't be done. You shouldn't be inflating fragments inside other fragments. From Android's documentation: Note: You cannot inflate a layout into a fragment when that layout includes a <fragment>. Nested fragments are only supported when added to a fragment dynamically. While you may be able to accomplish the task with the hacks presented here, I highly suggest you don't do it. Its impossible to be sure that these hacks will handle what each new Android OS does when you try to inflate a layout for a fragment containing another fragment. The only Android-supported way to add a fragment to another fragment is via a transaction from the child fragment manager. Simply change your XML layout into an empty container (add an ID if needed): <?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> Then in the Fragment onViewCreated(View view, #Nullable Bundle savedInstanceState) method: #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); }
I had the same issue and was able to resolve it by manually removing the MapFragment in the onDestroy() method of the Fragment class. Here is code that works and references the MapFragment by ID in the XML: #Override public void onDestroyView() { super.onDestroyView(); MapFragment f = (MapFragment) getFragmentManager() .findFragmentById(R.id.map); if (f != null) getFragmentManager().beginTransaction().remove(f).commit(); } If you don't remove the MapFragment manually, it will hang around so that it doesn't cost a lot of resources to recreate/show the map view again. It seems that keeping the underlying MapView is great for switching back and forth between tabs, but when used in fragments this behavior causes a duplicate MapView to be created upon each new MapFragment with the same ID. The solution is to manually remove the MapFragment and thus recreate the underlying map each time the fragment is inflated. I also noted this in another answer [1].
This is my answer: 1, Create a layout xml like following: <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, in the Fragment class, add a google map programmatically. 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(); } }
As mentioned by #Justin Breitfeller, #Vidar Wahlberg solution is a hack which might not work in future version of Android. #Vidar Wahlberg perfer a hack because other solution might "cause the map to be recreated and redrawn, which isn't always desirable". Map redraw could be prevented by maintaining the old map fragment, rather than creating a new instance every time. #Matt solution doesn't work for me (IllegalStateException) As quoted by #Justin Breitfeller, "You cannot inflate a layout into a fragment when that layout includes a . Nested fragments are only supported when added to a fragment dynamically." My solution: #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; }
Declare SupportMapFragment object globally private SupportMapFragment mapFragment; In onCreateView() method put below code mapFragment = (SupportMapFragment) getChildFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); In onDestroyView() put below code #Override public void onDestroyView() { super.onDestroyView(); if (mapFragment != null) getFragmentManager().beginTransaction().remove(mapFragment).commit(); } In your xml file put below code <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" /> Above code solved my problem and it's working fine
I would recommend replace() rather than attach()/detach() in your tab handling. Or, switch to ViewPager. Here is a sample project showing a ViewPager, with tabs, hosting 10 maps.
Another solution: if (view == null) { view = inflater.inflate(R.layout.nearbyplaces, container, false); } That's it, if not null you don't need to reinitialize it removing from parent is unnecessary step.
You are returning or inflating layout twice, just check to see if you only inflate once.
I have lost hours today to find the reason, fortunately this issue is not because of MapFragment implementation, fnfortunately, this does not work because nested fragments are only supported through support library from rev 11. My implementation has an activity with actionbar (in tabbed mode) with two tabs (no viewpager), one having the map and the other having a list of entries. Of course I've been quite naive to use MapFragment inside my tab-fragments, et voila the app crashed everytime I switched back to map-tab. ( The same issue I also would have in case my tab-fragment would inflate any layout containing any other fragment ). One option is to use the MapView (instead of MapFragment), with some overhead though ( see MapView Docs as drop-in replacement in the layout.xml, another option is to use support-library up from rev. 11 but then take programmatic approach since nested fragments are neither supported via layout. Or just working around programmatically by explicitely destroying the fragment (like in the answer from Matt / Vidar), btw: same effect is achieved using the MapView (option 1). But actually, I did not want to loose the map everytime I tab away, that is, I wanted to keep it in memory and cleanup only upon activity close, so I decided to simply hide/show the map while tabbing, see FragmentTransaction / hide
For those who are still running into this issue, the best way to make sure you don't get this error with a Map in a Tab is to make the Fragment extend SupportMapFragment instead of nesting a SupportMapFragment inside the Fragment used for the Tab. I just got this working using a ViewPager with a FragmentPagerAdapter, with the SupportMapFragment in the third Tab. Here is the general structure, note there is no need to override the onCreateView() method, and there is no need to inflate any layout 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))); } }); } } Result: Here is the full class code that I used to test with, which includes the placeholder Fragment used for the first two Tabs, and the Map Fragment used for the third Tab: 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"); } }); } } }
I respect all the answers but i found this one liner solution: If n Is the Number of tabs then: mViewPager.setOffscreenPageLimit(n); Example: In case mentioned : mViewPager.setOffscreenPageLimit(2); View pager implements a queue so, you don't have to let it remove that fragment. onCreateView is called only once.
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; } }
Nested fragments are not currently supported. Try Support Package, revision 11.
Have you been trying to reference your custom MapFragment class in the layout file? <?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>
If you will use only Vidar Wahlberg answer, you get error when you open other activity (for example) and back to map. Or in my case open other activity and then from new activity open map again( without use back button). But when you combine Vidar Wahlberg solution and Matt solution you will have not exceptions. layout <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> Fragment #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(); }
I had this in viewPager and the crash was because any fragment had to have its own tag, duplicate tags or ids for same fragment are not allowed.
I think there was some bugs in previous App-Compat lib for child Fragment. I tried #Vidar Wahlberg and #Matt's ans they did not work for me. After updating the appcompat library my code run perfectly without any extra effort.
Things to Note here is your app will crash badly in either of two cases:- 1) In order to reuse fragment with Maps again MapView Fragment must be removed when your fragment showing Maps got replaced with other fragment in onDestroyView callback. else when you try to inflate same fragment twice Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment error will happen. 2) Secondly you must not mix app.Fragment operations with android.support.v4.app.Fragment API operations eg.do not use android.app.FragmentTransaction to remove v4.app.Fragment type MapView Fragment. Mixing this will again result into crash from fragment side. Here is sample code snippet for correct usage of 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" /> Result looks like this :- Hope it will help Somebody.
In this solution you do not need to take static variable; 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()); } }); }`
Try setting an id (android:id="#+id/maps_dialog") for your mapView parent layout. Works for me.
Anyone coming here now that's getting this type of error when opening a Dialog or other Fragment with the Google Places AutocompleteSupportFragment, try this one-liner (I don't know how safe this is but it works for me): autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit(); before you dismiss/destroy your fragment.
<?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> 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.