How to use Google Map's V2 MapView for Android

2019-08-03 14:35发布

问题:

I am trying to display the actionbar above Google Maps, but I realize that once Google Maps open, it is not using my app, but Google's app, therefore the title bar disappears.

I understand that using a MapView allows me to achieve this but I'm not sure how to transition from using a fragment to a MapView as I am getting a NullPointerException.

Here's the AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.test" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/logo"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="@string/google_maps_key" />

        <activity
            android:name=".MapActivity"
            android:label="@string/title_activity_map" >
        </activity>

    </application>

</manifest>

My activity_map.xml:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/map"
    tools:context="org.test.MapActivity"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    map:cameraTargetLat="35.25215"
    map:cameraTargetLng="-112.24659"
    map:uiTiltGestures="true"
    map:uiZoomGestures="true"
    map:uiZoomControls="true" />

My activity_mapview.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">

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

</LinearLayout>

My MapActivity.java:

public class MapActivity extends FragmentActivity {
  private GoogleMap mMap;
  private MapView mapView;
  private Marker marker;

  private static final LatLng ONE = new LatLng(32.882216, -117.222028);
  private static final LatLng TWO = new LatLng(32.872000, -117.232004);
  private static final LatLng THREE = new LatLng(32.880252, -117.233034);
  private static final LatLng FOUR = new LatLng(32.885200, -117.226003);

  private ArrayList<LatLng> coords = new ArrayList<LatLng>();
  private static final int POINTS = 4;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_mapview);
    mapView = (MapView) findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);

    setUpMapIfNeeded();
  }

  @Override
  protected void onStop() {
    super.onStop();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mapView.onResume();
    setUpMapIfNeeded();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    mapView.onDestroy();
  }

  @Override
  public void onLowMemory() {
    super.onLowMemory();
    mapView.onLowMemory();
  }

  @Override
  protected void onPause() {
    super.onPause();
    mapView.onPause();
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mapView.onSaveInstanceState(outState);
  }

  private void setUpMapIfNeeded() {
    // Do a null check to confirm that we have not already instantiated the map.
    if (mMap == null) {
      Log.d("Test", "GO HERE");
      mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
      Log.d("Test", "GO THERE");
      if (mMap != null) {
        coords.add(ONE);
        coords.add(TWO);
        coords.add(THREE);
        coords.add(FOUR);

        setUpMap();
      }
    }
  }

  private void setUpMap() {
    for (int i = 0; i < POINTS; i++) {
      mMap.addMarker(new MarkerOptions().position(coords.get(i))
          .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)));
    }
  }
}

The styles.xml:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>

    <style name="FullscreenTheme" parent="android:Theme.NoTitleBar">
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowBackground">@null</item>
        <item name="metaButtonBarStyle">@style/ButtonBar</item>
        <item name="metaButtonBarButtonStyle">@style/ButtonBarButton</item>
    </style>

    <!-- Backward-compatible version of ?android:attr/buttonBarStyle -->
    <style name="ButtonBar">
        <item name="android:paddingLeft">2dp</item>
        <item name="android:paddingTop">5dp</item>
        <item name="android:paddingRight">2dp</item>
        <item name="android:paddingBottom">0dp</item>
        <item name="android:background">@android:drawable/bottom_bar</item>
    </style>

    <!-- Backward-compatible version of ?android:attr/buttonBarButtonStyle -->
    <style name="ButtonBarButton" />

Based on doing some log statements, it appears as though GO HERE is printed out but it never reaches GO THERE, so the issue lies in the following code, but I'm not sure how to fix it, as I'm new to Android programming:

mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                .getMap();

Logcat:

08-21 01:29:20.593  30698-30698/org.test E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{org./org.test.MapActivity}: java.lang.NullPointerException
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
            at android.app.ActivityThread.access$1500(ActivityThread.java:117)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:130)
            at android.app.ActivityThread.main(ActivityThread.java:3683)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:507)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NullPointerException
            at 
org.test.MapActivity.setUpMapIfNeeded(MapActivity.java:183)
                at 
org.test.MapActivity.onCreate(MapActivity.java:77)
                at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
                at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
                at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
                at android.app.ActivityThread.access$1500(ActivityThread.java:117)
                at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
                at android.os.Handler.dispatchMessage(Handler.java:99)
                at android.os.Looper.loop(Looper.java:130)
                at android.app.ActivityThread.main(ActivityThread.java:3683)
                at java.lang.reflect.Method.invokeNative(Native Method)
                at java.lang.reflect.Method.invoke(Method.java:507)
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
                at dalvik.system.NativeStart.main(Native Method)

Thanks.

回答1:

Define dependencies in your build.gradle

compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.google.android.gms:play-services:7.8.0'

For getting title extend your activity from AppCompatActivity and set title with setTitle()

For using MapView you don't need to make any checks in onResume.
Just get your map by async method in onCreate.
This setUpMap method will be called when map is ready.

public class MapActivity extends AppCompatActivity {
    private GoogleMap mMap;
    private MapView mapView;
    private Marker marker;

    private static final LatLng ONE = new LatLng(32.882216, -117.222028);
    private static final LatLng TWO = new LatLng(32.872000, -117.232004);
    private static final LatLng THREE = new LatLng(32.880252, -117.233034);
    private static final LatLng FOUR = new LatLng(32.885200, -117.226003);

    private ArrayList<LatLng> coords = new ArrayList<LatLng>();
    private static final int POINTS = 4;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mapview);
        setTitle("My Map");
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                setUpMap(googleMap);
            }
        });
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }
    @Override
    protected void onResume() {
        super.onResume();
        mapView.onResume();
    }
    @Override
    protected void onPause() {
        super.onPause();
        mapView.onPause();
    }
    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }
    private void setUpMap(GoogleMap map) {
        mMap = map;

        coords.add(ONE);
        coords.add(TWO);
        coords.add(THREE);
        coords.add(FOUR);
        for (int i = 0; i < POINTS; i++) {
            mMap.addMarker(new MarkerOptions()
                    .position(coords.get(i))
                    .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)));
        }
    }
}


回答2:

There is one major problem here, don't use the com.google.android.gms.maps.MapView in your activity_mapview.xml rather include the MapFragment in your activity_mapview.xml like this:

<LinearLayout
    xmlns:map="http://schemas.android.com/apk/res-auto"
    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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/map"
    tools:context="org.test.MapActivity"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    map:cameraTargetLat="35.25215"
    map:cameraTargetLng="-112.24659"
    map:uiTiltGestures="true"
    map:uiZoomGestures="true"
    map:uiZoomControls="true" />

</LinearLayout>

or use a FrameLayout and replace it with the SupportMapFragment, let me know if this doesn't work.



回答3:

SupportMapFragment fm = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);

        map = fm.getMap();
        if (map != null) {
       //Do some thing here

          }

map declaration should be outside of if condition



回答4:

please change setContentView(R.layout.activity_mapview) to setContentView(R.layout.activity_map).

Because here

((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                .getMap();

you did not inflate layout which contains this fragment.