I'm trying to use a Google map in a project I'm working on and am having trouble getting the map to work as a fragment. I want to use the map as a fragment and add the fragment to my main activity (with the assumption that I could reuse this fragment somewhere else if I needed) but the app either crashes or fails to do anything other than load the map, depending on what I try and change.
Here's the tutorial that I used as a basis for the code in my MapsFragment.java file. https://developers.google.com/maps/documentation/android-api/current-places-tutorial
Here's the basic tag I'm using to try and display the map from within my activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_height="wrap_content" android:layout_width="match_parent" android:text="text"/> <fragment xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/map_fragment" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="[path info].MapsFragment"/> </LinearLayout> @Override public void onConnected(Bundle connectionHint) { getDeviceLocation(); SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map_fragment); mapFragment.getMapAsync(this); } With this XML the map will load, but it doesn't seem to call any of the methods within my Fragment class. It certainly doesn't call onCreate, onCreateView, or onConnected.
And here's the code for my fragment class, MapsFragment.java
import android.Manifest; import android.app.Activity; import android.content.pm.PackageManager; import android.location.Location; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.os.Bundle; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.location.LocationListener; import java.util.Date; public class MapsFragment extends Fragment implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { private static final String TAG = MapsActivity.class.getSimpleName(); private GoogleMap mMap; // A default location (Sydney, Australia) and default zoom to use when location permission is // not granted. private final LatLng mDefaultLocation = new LatLng(-33.8523341, 151.2106085); private static final int DEFAULT_ZOOM = 15; private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1; private boolean mLocationPermissionGranted; private Location mCurrentLocation; private Marker mCurrentLocationMarker; private CameraPosition mCameraPosition; private GoogleApiClient mGoogleApiClient; private LocationRequest mLocationRequest; private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000; private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = UPDATE_INTERVAL_IN_MILLISECONDS / 2; // Keys for storing activity state. private static final String KEY_CAMERA_POSITION = "camera_position"; private static final String KEY_LOCATION = "location"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // initialize GoogleAPIClient Activity parentActivity = this.getActivity(); if (parentActivity != null) { mGoogleApiClient = new GoogleApiClient.Builder(this.getActivity()) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); createLocationRequest(); } } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.activity_maps, container, false); } @Override public void onResume() { super.onResume(); //setUpMapIfNeeded(); if (mGoogleApiClient.isConnected()) { getDeviceLocation(); } else { mGoogleApiClient.connect(); } } @Override public void onPause() { super.onPause(); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; if (mCurrentLocation != null) { LatLng latLng = new LatLng(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude()); // maybe add default zoom? mMap.addMarker(new MarkerOptions().position(latLng).title("You")); //(latLng).title("Marker")); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, DEFAULT_ZOOM)); } else { Log.d(TAG, "Current location is null. Using defaults."); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mDefaultLocation, 10)); //zoom to Sydney, Australia } } @Override public void onConnected(Bundle connectionHint) { getDeviceLocation(); SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map_fragment); mapFragment.getMapAsync(this); } @Override public void onConnectionFailed(@NonNull ConnectionResult result) { // Refer to the reference doc for ConnectionResult to see what error codes might // be returned in onConnectionFailed. Log.d(TAG, "Play services connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode()); } @Override public void onConnectionSuspended(int cause) { Log.d(TAG, "Play services connection suspended"); } @Override public void onLocationChanged(Location location) { mCurrentLocation = location; //updateMarkers(); if (mCurrentLocationMarker != null) { mCurrentLocationMarker.remove(); } LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); // DEBUG Date date = new Date(); int hour = date.getHours(); int minutes = date.getMinutes(); String time = String.valueOf(hour) + ":" + String.valueOf(minutes); // mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, DEFAULT_ZOOM)); // might need mMap.animateCamera(CameraUpdateFactory.zoomTo(11)) to animate transition. Not sure. MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(latLng); markerOptions.title(time); mCurrentLocationMarker = mMap.addMarker(markerOptions); Log.d(TAG, latLng.toString() + ", Time: " + time); } private void getDeviceLocation() { if (ContextCompat.checkSelfPermission(this.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { mLocationPermissionGranted = true; } else { ActivityCompat.requestPermissions(this.getActivity(), new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); } if (mLocationPermissionGranted) { mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { mLocationPermissionGranted = false; switch (requestCode) { case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { mLocationPermissionGranted = true; } } } updateLocationUI(); } @SuppressWarnings("MissingPermission") private void updateLocationUI() { if (mMap == null) { return; } if (mLocationPermissionGranted) { mMap.setMyLocationEnabled(true); mMap.getUiSettings().setMyLocationButtonEnabled(true); } else { mMap.setMyLocationEnabled(false); mMap.getUiSettings().setMyLocationButtonEnabled(false); mCurrentLocation = null; } } private void createLocationRequest() { mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } } I've tried getting rid of the android:name name-value pair, and adding class="[fully qualified path name]" in the XML because I figured I needed a way to tie the class and the fragment together so that the code would be executed when the fragment is inflated. If I do this my class code is actually called, but I get the following error when getMapAsync is called within onConnected
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.gms.maps.SupportMapFragment.getMapAsync(com.google.android.gms.maps.OnMapReadyCallback)' on a null object reference at [the path info].MapsFragment.onConnected(MapsFragment.java:163) at com.google.android.gms.common.internal.zzm.zzq(Unknown Source) at com.google.android.gms.internal.zzaal.zzo(Unknown Source) at com.google.android.gms.internal.zzaaj.zzvE(Unknown Source) at com.google.android.gms.internal.zzaaj.onConnected(Unknown Source) at com.google.android.gms.internal.zzaan.onConnected(Unknown Source) at com.google.android.gms.internal.zzzy.onConnected(Unknown Source) at com.google.android.gms.common.internal.zzl$1.onConnected(Unknown Source) at com.google.android.gms.common.internal.zzf$zzj.zzwZ(Unknown Source) at com.google.android.gms.common.internal.zzf$zza.zzc(Unknown Source) at com.google.android.gms.common.internal.zzf$zza.zzu(Unknown Source) at com.google.android.gms.common.internal.zzf$zze.zzxa(Unknown Source) at com.google.android.gms.common.internal.zzf$zzd.handleMessage(Unknown Source) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) I've been trying different things and trying to understand what exactly is causing my problem and how to get this working but to no avail. If this isn't the right way to go about what I'm trying to do, what should I do instead?
I tried looking at questions with similar sounding problems on this site but none of them seemed to have an answer for me.
Thanks for your help