Android Maps V2 MapView inside custom fragment (NPE)

23,428

Solution 1

I succeeded in including a MapView (v2) in a custom Fragment itself embedded in a ViewPager. In my case, the MapView is included in the Fragment layout file. I have had to call lifecycle methods on MapView (onCreate() called onCreateView() from the Fragment), and to call manually MapsInitializer.initialize(context) to avoid a NullPointerException from class BitmapDescriptorFactory (to get the bitmap for markers). This last trick is strange, and I don't know why the Map system isn't properly initialized itself without this call, maybe it's just a bug in the current version ...

In my case I haven't had any NullPointerException in onResume() or onDestroy().

Solution 2

I struggled a bit with PoPy's answer but in the end I managed it and here is what I came up with. Probably this is helpful to others which will also come across this issue:

public class MyMapFragment extends Fragment {

    private MapView mMapView;
    private GoogleMap mMap;
    private Bundle mBundle;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View inflatedView = inflater.inflate(R.layout.map_fragment, container, false);

        try {
            MapsInitializer.initialize(getActivity());
        } catch (GooglePlayServicesNotAvailableException e) {
            // TODO handle this situation
        }

        mMapView = (MapView) inflatedView.findViewById(R.id.map);
        mMapView.onCreate(mBundle);
        setUpMapIfNeeded(inflatedView);

        return inflatedView;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBundle = savedInstanceState;
    }

    private void setUpMapIfNeeded(View inflatedView) {
        if (mMap == null) {
            mMap = ((MapView) inflatedView.findViewById(R.id.map)).getMap();
            if (mMap != null) {
                setUpMap();
            }
        }
    }

    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
    }

    @Override
    public void onResume() {
        super.onResume();
        mMapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mMapView.onPause();
    }

    @Override
    public void onDestroy() {
        mMapView.onDestroy();
        super.onDestroy();
    }
}

And here is the corresponding res/layout/map_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" >

    <com.google.android.gms.maps.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

You can omit the RelativeLayout (and move the xmlns declartion up to your new root element, in this case the com.google.android.gms.maps.MapView) if you have just one element in your layout like in this example.

Share:
23,428

Related videos on Youtube

dnkoutso
Author by

dnkoutso

Updated on June 19, 2020

Comments

  • dnkoutso
    dnkoutso almost 4 years

    I do not want to use or extend SupportMapFragment or MapFragment. I have my own base class with a bunch of code in it.

    The documentation clearly states that when someone uses MapView by itself, all corresponding lifecycle methods (onCreate() onResume() etc.) should be called.

    Most of the lifecycle methods in a Fragment are similar to an Activity but when I switch back and forth between my Fragment I eventually get an obfuscated NPE in onDestroy() or in onResume() methods.

    All the samples provided use an Activity with a MapView but not a custom Fragment.

    Has someone done that already? Can you provide sample code of a MapView in your own Fragmentclass?

  • PoPy
    PoPy over 11 years
    Here are some details about MapsInitializer.initialize(context) from the official documentation: BitmapDescriptorFactory MapsInitializer
  • dnkoutso
    dnkoutso over 11 years
    clear I do have a Map object already but for some reason the exception is still thrown. It ended up working when I added this static ugly call.
  • Sébastien BATEZAT
    Sébastien BATEZAT about 11 years
    @PoPy I'm trying to do the same as yourself (i.e : including mapView in a viewPager), but how do you do to keep mapView state (markers, position, zoom, ...). My state is lost when I go from page 1 (with map) to page 3, and return to page 1 !
  • Sébastien BATEZAT
    Sébastien BATEZAT about 11 years
    Thanks for your solution, it helped me. However, are you using this in a view pager ? Because I have a problem with this solution and a view pager. I don't know how to keep map state (see my comment on PoPy's answer for more details). Could you help me ?
  • Hesam
    Hesam about 11 years
    Very useful, Thanks Jens ;)
  • Khobaib
    Khobaib about 11 years
    @jens thanx for the code, it works for the first fragment of my pager but when I swipe to 2nd fragment, the MapView shows nothing - it's like there was nothing in that frame. FYI, the fragment transactions are done in the parent fragment's onCreateView method. Another issue I have found - I have implemented setOnMapClickListener for the GoogleMap object but it seems all the clicks are not handled properly. It gets some of the clicks, not all. Don't know what happens here.
  • Khobaib
    Khobaib about 11 years
    Update - It seems that we have to call with getChildFragmentManager() from the parent fragment instead of getFragmentManager() to avoid the issue that map comes in only one fragment. But while swipe in pager, I am getting some NullPointerException.
  • PoPy
    PoPy about 11 years
    @Sebastien I haven't tried to save the state for the moment, so sorry but i can't help you on this
  • pvshnik
    pvshnik over 10 years
    Thanks for your implementation. I've noticed one issue. When restoring this map fragment from backstack, map camera position is lost. In my current implementation I use OnCameraChangeListener to save camera position each time it changes in fragment instance variable. When restoring fragment from backstack, I check this instance variable and if it is not null, move map camera to this saved position.
  • YuDroid
    YuDroid almost 10 years
    Then how to add/use this MyMapFragment in my Activity? Can you please provide the complete example? I want to add mapview dynamically and moreover want to set width and height of mapview based on screen width and height. Please guide me for this..
  • Jens Kohl
    Jens Kohl almost 10 years
    @YuDroid You should create a new question here on SO for that. Because that's out of scope of the OP.
  • Pijusn
    Pijusn almost 10 years
    @Sebastien I believe MapView.onSaveInstaceState method is for the problem you are having.
  • laaptu
    laaptu almost 10 years
    @martyonair: Where have I called its twice? I have said at Activity or Fragment where you are using it. Please read it nicely and then only downvote
  • Marcel Bro
    Marcel Bro over 9 years
    @laaptu Where did you initialize mapView?
  • laaptu
    laaptu over 9 years
    @anoniim You can initialize either on Activity or Fragment,wherever you are using it
  • Marcel Bro
    Marcel Bro over 9 years
    @pvshnik What callback do you use for restoring fragment from backstack, please?
  • Marcel Bro
    Marcel Bro over 9 years
    @laaptu I mean what method? Can't see it in your code snippet.
  • laaptu
    laaptu over 9 years
    @anoniim You can add like findViewById(R.id.yourgivenid) on Activity onCreate() after setting setContentView or on Fragment onCreateView
  • mmathieum
    mmathieum over 9 years
    I had to call MapView.onDestroy() and clear the MapView reference inside Fragment.onDestroyView() or else the resumed fragment was showing an empty gray map (kinda make sense since calling MapView.onCreate() inside Fragment.onCreateView()).