1

Desired Behavior

In case it affects any answers or suggestions, what I would like to ultimately see in this app is a navigation UI where one of the Fragment is a map. I used a template to start with a navigation UI, which gave me several Fragments, one for each tab. Currently, I'm trying to use the Google API to put a MapView inside of one of those Fragments.

Current Behavior

Generally, this isn't working (obviously). I have everything up and running, but when I pull up the tab with the map in it, the map is blank. Nothing crashes, and no errors are output:

I/Google Maps Android API: Google Play services client version: 12451000
I/Google Maps Android API: Google Play services package version: 201516040
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
I/DynamiteModule: Considering local module com.google.android.gms.googlecertificates:0 and remote module com.google.android.gms.googlecertificates:4
    Selected remote version of com.google.android.gms.googlecertificates, version >= 4

Other Details/Troubleshooting

I know that my API key works because I have another app in which the maps work as expected. I doubt I need a second API key, but I looked anyway and since it wasn't overly obvious how to create a second API key, I think that hunch is correct. Most of the code I'm using was copied from that working example; the primary difference is that other example uses a SupportMapFragment and it has a class MapsActivity extends FragmentActivity. Here, since I don't really want to deal with nesting fragments, I'm trying to use a MapView inside that fragment.

What bits of my code that weren't copied largely came from online sources. None of what I've tried has produced better results.

MapView inside fragment is answering a different problem

Creating google maps v2 in a fragment using Navigation Drawer contains only things I already have

android MapView in Fragment gave me the idea to use MapsInitializer.initialize(this.getActivity()); but that didn't help

Mapview in separate fragment android has nothing new

how do i solve integrating google map in fragment in android uses a Fragment not a MapView

I also know that onMapReady is being called. Inside of that, I move the camera and that subsequently calls onCameraMoveStarted. However, it does not enter onCameraMove or onCameraIdle.

Code

This is the code as it is, which uses some of the ideas from the above links. I tried to cut out anything that's unrelated (other fragments, etc)

AndroidManifest.xml

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

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:windowSoftInputMode="stateVisible|adjustResize" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>

</manifest>

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        app:menu="@menu/bottom_nav_menu" />

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/mobile_navigation"
        tools:context=".MainActivity" />

</androidx.constraintlayout.widget.ConstraintLayout>

mobile_navigation.xml

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_map">

    <fragment
        android:id="@+id/navigation_map"
        android:name="com.example.retailtherapy.ui.map.MapFragment"
        android:label="Map"
        tools:layout="@layout/fragment_map" />

</navigation>

fragment_map.xml


<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.map.MapFragment" >

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

</RelativeLayout>

MapFragment.java

public class MapFragment extends Fragment implements OnMapReadyCallback, LocationListener {

    /*************************************************************************
     *
     * M E M B E R S
     *
     */

    // Location manager for getting current location
    private LocationManager mLocationManager = null;

    // The map object for reference for ease of adding points and such
    private GoogleMap mGoogleMap = null;
    private MapView mMapView = null;

    // The camera position tells us where the view is on the map
    private CameraPosition mCameraPosition = Constants.GetDefaultCameraPosition();

    // Current latitude and longitude of user
    private LatLng mCurrentLatLong = new LatLng(0.0, 0.0);

    // These are flags that allow us to selectively not move the camera and
    // instead wait for idle so the movement isn't jerky
    private boolean mCameraMoving = false;
    private boolean mPendingUpdate = false;

    /*************************************************************************
     *
     * F R A G M E N T
     *
     */

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        super.onCreateView(inflater, container, savedInstanceState);
        View root = inflater.inflate(R.layout.fragment_map, container, false);

        // The google map...
        MapsInitializer.initialize(getActivity());
        mMapView = (MapView) root.findViewById(R.id.googleMap);
        mMapView.onCreate(savedInstanceState);
        mMapView.getMapAsync(this);

        // The required permissions...
        final String[] REQUIRED_PERMISSIONS = {
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.INTERNET,
        };
        requestPermissions(REQUIRED_PERMISSIONS, 0);

        return root;
    }

    @Override
    public void onStart() throws SecurityException {
        super.onStart();

        // Location manager for getting info current location
        mLocationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this);
    }

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

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

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

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mMapView.onSaveInstanceState(outState);
    }

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

    /*************************************************************************
     *
     * G O O G L E   M A P S
     *
     */

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mGoogleMap = googleMap;

        // Assuming if here that we have permissions okay.  Dangerous sure
        // but I'm lazy...
        mGoogleMap.setMyLocationEnabled(true);

        // Store the location of the camera for UI purposes so we can go
        // back to where we were
        mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
            @Override
            public void onCameraMove() {
                mCameraPosition = mGoogleMap.getCameraPosition();
            }
        });
        mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
            @Override
            public void onCameraMoveStarted(int i) {
                mCameraMoving = true;
            }
        });
        mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
            @Override
            public void onCameraIdle() {
                mCameraMoving = false;
                if (mPendingUpdate) {
                    centerCameraOnLocation(false);
                }
            }
        });

        // Start with user centered in view
        centerCameraOnLocation(true);
    }

    /*************************************************************************
     *
     * L O C A T I O N
     *
     */

    @Override
    public void onLocationChanged(Location location) {
        // Store the current location so if we start auto-center but aren't
        // already, we know where to pan to
        LatLng newLatLng = new LatLng(location.getLatitude(), location.getLongitude());
        if (!newLatLng.equals(mCurrentLatLong)) {
            mCurrentLatLong = new LatLng(location.getLatitude(), location.getLongitude());
            centerCameraOnLocation(false);
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {}

    @Override
    public void onProviderDisabled(String provider) {}

    @Override
    public void onProviderEnabled(String provider) {}

    /*************************************************************************
     *
     * M A P   C A M E R A
     *
     */

    /*
     * Updates the camera position based on current state.  If the camera is
     * already moving, then wait.
     */
    private void centerCameraOnLocation(boolean animate) {
        if (mGoogleMap == null) {
            return;
        }

        if (mCameraMoving) {
            mPendingUpdate = true;
            return;
        }

        // Get the CameraPosition to update to based on whether we are auto
        // centering or not.
        if (mCameraPosition == null) {
            mCameraPosition = Constants.GetDefaultCameraPosition();
        }

        // If this is the same as it was before, then don't reset it
        if (mCameraPosition.equals(mGoogleMap.getCameraPosition())) {
            return;
        }

        // Make the update so we can move the camera
        CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(mCameraPosition);

        // Move the camera with or without animation.  The time we don't want
        // animation is when we're setting the position initially
        if (animate) {
            mGoogleMap.animateCamera(cameraUpdate);
        }
        else {
            mGoogleMap.moveCamera(cameraUpdate);
        }
        mPendingUpdate = false;
    }
}
TMenninger
  • 33
  • 1
  • 5

2 Answers2

0

FINALLY I found the answer in a comment at https://stackoverflow.com/a/51464293/3038157.

Basically, it seems there was something stale. Without changing any code, I uninstalled the app, cleaned the build, rebuilt, and voila it worked.

TMenninger
  • 33
  • 1
  • 5
0

You must forward all the life cycle methods from the Activity or Fragment containing this view to the corresponding ones in this class. View this official guide or example code

 // *** IMPORTANT ***
 // MapView requires that the Bundle you pass contain _ONLY_ MapView SDK
 // objects or sub-Bundles.
Pham Hung
  • 308
  • 1
  • 4
  • 14