I'm using a GoogleMap/MapView in a layout, but as a View not a Fragment (because the parent needs to be a Fragment), so the fragment's layout includes this:
<com.google.android.gms.maps.MapView
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
The Fragment contains this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
<......>
initMap(bundle, mapView);
return rootView;
}
#Override
public void onResume() {
super.onResume();
MyApp.bus.register(this);
updateMapLocation(MyApp.getMostRecentLocation());
}
#Override
public void onPause() {
super.onPause();
MyApp.bus.unregister(this);
}
#Subscribe
public void locationReceived(LocationReceived m) {
Timber.i("Received bus message - Location!");
updateMapLocation(MyApp.getMostRecentLocation());
}
Its parent Fragment contains this:
private MapView mapView;
private GoogleMap map;
#Override
public void onResume() {
super.onResume();
if (mapView!=null) {
mapView.onResume();
map = this.mapView.getMap();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mapView!=null) mapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
if (mapView!=null) mapView.onLowMemory();
}
protected void initMap(Bundle bundle, MapView mapView) {
this.mapView = mapView;
this.mapView.onCreate(bundle);
map = this.mapView.getMap();
map.getUiSettings().setMyLocationButtonEnabled(false);
map.setMyLocationEnabled(true);
map.setBuildingsEnabled(true);
map.getUiSettings().setZoomControlsEnabled(false);
map.getUiSettings().setMyLocationButtonEnabled(true);
try {
MapsInitializer.initialize(this.getActivity());
} catch (Exception e) {
Timber.e(e, "Error initialising Google Map");
}
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(MyApp.getCentralUKLatLng(), getResources().getInteger(R.integer.map_zoom_initial));
map.animateCamera(cameraUpdate);
}
#Override
public void onPause() {
super.onPause();
if (mapView!=null) mapView.onPause();
}
protected void updateMapLocation(Location location) {
Timber.i("Moving map to a new location: " + location);
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 15);
map.animateCamera(cameraUpdate);
}
New locations are being delivered via an Otto bus. The code above works perfectly, but ONLY the first time. If I open another fragment in front of this one, and then close it again, the map fails to animate to subsequently provided locations. The locations are definitely received, the animateCamera() method is definitely called (with a valid location and zoom) but absolutely nothing happens. No error, no log message, nothing. What makes it even more infuriating is on one Fragment (which is identical to the code above) it works fine when resuming the Fragment.
I assume I am doing something wrong with how the GoogeMap or the MapView is being (re)initialised on resume, but I'm passing through the onPause() and onResume() calls to the MapView which I understand is necessary. What else do I need to do?
This is because you have not write the code in onResume().
See when you open and close the second fragment the first fragment will always stay there so when the second fragment gets closed the onResume() of first will get called.
So you have to animate the camera in onResume().
Related
So, I basically put a GoogleMaps API inside a fragment in my app, and I want to give it a specific default search like we do with Intent, but in this case im using fragment instead of an intent.`public class LocalGymsFragment extends Fragment {
MapView mMapView;
private GoogleMap googleMap;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_local_gyms, container, false);
mMapView = (MapView) rootView.findViewById(R.id.mapView);
mMapView.onCreate(savedInstanceState);
mMapView.onResume(); // needed to get the map to display immediately
try {
MapsInitializer.initialize(getActivity().getApplicationContext());
} catch (Exception e) {
e.printStackTrace();
}
mMapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap mMap) {
googleMap = mMap;
// For dropping a marker at a point on the Map
LatLng sydney = new LatLng(-34, 151);
googleMap.addMarker(new MarkerOptions().position(sydney).title("Marker Title").snippet("Marker Description"));
// For zooming automatically to the location of the marker
CameraPosition cameraPosition = new CameraPosition.Builder().target(sydney).zoom(12).build();
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
});
return rootView;
}
#Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
}`
I suggest that you try implementing the GoogleMaps API on a constraint layout or new GoogleMaps Activity. This is because Fragments aren't just a cup of tea. It is tough implementing this on fragments.
I'm using the play services v9.8.0 (without location service permissions) and am still facing a leak when I use a MapView in a dialog fragment. I'm using it like in my code example and I use it to display a location only and I DON'T have setMyLocationEnabled (as I don't even have the permissions for this setting).
Does anyone see a problem in my code? I'm getting a leak like the one here: MapView v2 keeping Context around. I do following:
create a dialog
replace a view in my layout with a MapView (because I allow to use static maps as well, so my default view is a ImageView in my layout, that will be replaced with a MapView)
Then it happens that my fragments leaks MapView.mContext...
Code - Dialog Fragment
public class DialogMediaDetails extends DialogFragment
{
private GoogleMap mGoogleMap = null;
private MapView mMapView = null;
#Override
public final Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog dlg = ...; // create dialog
View view = ...; // get view from dialog
Location location = ...; // defined location
initGoogleMap();
return dlg;
}
private void initGoogleMap(Location location)
{
mMapView = new MapView(getActivity());
MapsInitializer.initialize(getActivity());
// Updates the location and zoom of the MapView
mMapView.onCreate(null);
mMapView.getMapAsync(new OnMapReadyCallback()
{
#Override
public void onMapReady(GoogleMap googleMap)
{
LatLng coordinates = new LatLng(location.getLatitude(), location.getLongitude());
googleMap.addMarker(new MarkerOptions().position(coordinates));
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(coordinates, 15));
mGoogleMap = googleMap;
mMapView.onResume();
}
});
mMapView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
mMapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// MapView is scrollable, so we disable dragging
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
behavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() {
#Override
public boolean canDrag(AppBarLayout appBarLayout) {
return false;
}
});
}
});
replaceHeader(mMapView);
}
private void replaceHeader(View view)
{
ViewGroup parent = (ViewGroup) pbHeader.getParent();
int index = parent.indexOfChild(pbHeader);
ViewGroup.LayoutParams lp = pbHeader.getLayoutParams();
parent.removeView(pbHeader);
parent.addView(view, index, lp);
}
// ----------------------------------------
// forward all lifecycle events to MapView
// ----------------------------------------
#Override
public void onResume() {
super.onResume();
if (mMapView != null)
mMapView.onResume();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mMapView != null)
mMapView.onSaveInstanceState(outState);
}
#Override
public void onPause() {
super.onPause();
if (mMapView != null)
mMapView.onPause();
}
#Override
public void onLowMemory() {
super.onLowMemory();
if (mMapView != null)
mMapView.onLowMemory();
}
#Override
public void onDestroy() {
super.onDestroy();
if (mMapView != null)
mMapView.onDestroy();
mMapView = null;
}
}
There are some issues reported about leaks with MapView. You may try calling googleMap.setMyLocationEnabled(false); in onDestroy to prevent the leak from happening. Failure to call either MapView.onDestroy or GoogleMap.setMyLocationEnabled(false) will cause a leak. Here's a related thread which might help.
I had the same issue, and realized in my heap reference tree that the bug is coming from the lite mode part of the library. Try not using lite mode and adding all the corresponding life cycles for mapview.
In my Android Application I have a Fragment with a button. Once a the button is clicked I load another Fragment with a MapView. It all works good but the problem is that the Fragment with Google Maps lasts at least 0.5 seconds to launch once the button of the previous Fragment is clicked. Do you know another way to load google maps without stucking the Fragment transaction?
Here is the fragment that loads Google Maps
public class DetalleRuta extends android.support.v4.app.Fragment {
private GoogleMap googleMap;
private MapView mapView;
public DetalleRuta() {
// Required empty public constructor
}
#Override
public void onResume() {
mapView.onResume();
super.onResume();
}
#Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_detalle_ruta, container, false);
//Inicio el mapa
mapView = (MapView)v.findViewById(R.id.mapa);
mapView.onCreate(savedInstanceState);
googleMap = mapView.getMap();
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
return v;
}
}
Maybe my smartphone is not good enough, it is a BQ aquaris E4.
You are synchronously loading the map, try doing it asynchronously, Google maps has a Map Ready callback that you can use.
import com.google.android.gms.maps.*;
import com.google.android.gms.maps.model.*;
import android.app.Activity;
import android.os.Bundle;
public class MapPane extends Activity implements OnMapReadyCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.map_activity);
MapFragment mapFragment = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap map) {
LatLng sydney = new LatLng(-33.867, 151.206);
map.setMyLocationEnabled(true);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 13));
}
}
Adding lifecycle methods and calling mapView's lifecycle methods worked for me!
#Override
protected void onResume() {
mMapView.onResume();
super.onResume();
}
#Override
protected void onPause() {
mMapView.onPause();
super.onPause();
}
#Override
protected void onDestroy() {
mMapView.onDestroy();
super.onDestroy();
}
#Override
public void onLowMemory() {
mMapView.onLowMemory();
super.onLowMemory();
}
I have a fragment (ApartmentFragment) which is a page that show details of an apartment. I want to add a little map to show where the apartment is.
just for clarity, I'm using the MaterialNavigationDrawer that is on github, my app is composed by a single Activity (the navigation drawer) and many fragments.
Every stackoverflow post I have read didn't helped me. Many of them were a bit confusing. I don't want only the map inside ApartmentFragment, i want to show other things plus the map.
Which is the best way to do this?
Is possible to put a mapfragment inside a fragment? or I need to transform ApartmentFragment in activity?
most of what u read it's correct. You'll just use MapView instead of MapFragment
https://developer.android.com/reference/com/google/android/gms/maps/MapView.html
then on your ApartmenFragment you have to make all callbacks to mapview
onCreate(Bundle)
onResume()
onPause()
onDestroy()
onSaveInstanceState()
onLowMemory()
like this:
private MapView mapView;
#Nullable #Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// inflate your view `root` and then call ...
mapView = (MapView) root.indViewById(R.id.map_view);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
return root;
}
#Override public void onResume() {
super.onResume();
mapView.onResume();
}
#Override public void onPause() {
super.onPause();
mapView.onPause();
}
#Override public void onDestroyView() {
super.onDestroyView();
mapView.onDestroy();
}
#Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
#Override public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override public void onMapReady (GoogleMap googleMap) {
// from https://developer.android.com/reference/com/google/android/gms/maps/OnMapReadyCallback.html
// and here you can do your map stuff with `googleMap`
}
I'm using the solution here to add markers to a map in a Fragment and can't figure out why I can only add one marker.
I'm sure I'm missing something when I build the camera position, which is getting called twice in my test code. (See addLocationToMap below).
I am retrieving an ArrayList of geocoordinates on the main thread and want to loop through the list in the map tab calling addLocationToMap on each entry.
Here's my code:
public class RentalMapFragment extends Fragment {
MapView mMapView;
private GoogleMap googleMap;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// inflate and return the layout
View v = inflater.inflate(R.layout.fragment_map, container, false);
mMapView = (MapView) v.findViewById(R.id.mapView);
mMapView.onCreate(savedInstanceState);
mMapView.onResume();// needed to get the map to display immediately
try {
MapsInitializer.initialize(getActivity().getApplicationContext());
} catch (Exception e) {
e.printStackTrace();
}
googleMap = mMapView.getMap();
int level = 12;
addLocationToMap(17.385044, 78.486679, "Marker 2", level);
addLocationToMap(17.385044, 78.486671, "Marker 1", level);
// Perform any camera updates here
CameraPosition cameraPosition = new CameraPosition.Builder().target(new LatLng(17.385000, 78.486600)).zoom(level).build();
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
return v;
}
private void addLocationToMap(double latitude, double longitude, String markerTitle, int level) {
// Create lat/lon
LatLng latLng = new LatLng(latitude, longitude);
// create marker
MarkerOptions marker = new MarkerOptions().position(latLng).title(markerTitle);
// Changing marker icon
marker.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ROSE));
// adding marker
googleMap.addMarker(marker);
}
#Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
}
It turns out that I should not be creating the camera position after placing each marker. Rather, after adding all my markers I need to choose a postion for the camera to view the markers from.
I'm updating my question to demonstrate this.