remove and recreate a mapview in a fragment android - android

am using the android support v4- google maps by pete doyle and am trying to delete a mapview and then recreate it again, when i go away from and return to it, but i keep getting the usual Java.lang.IllegalStateException: You are only allowed to have a single MapView in a MapActivity. anyone know ho to solve this correctly. I have this code in my fragment.
Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Log.d(TAG, "onCreate View called here in location fragment!!");
if(view == null){
view = inflater.inflate(R.layout.map, container, false);
frame = (FrameLayout)view.findViewById(R.id.mapContainer);
//mapview = (MapView)view.findViewById(R.id.mapView);
}
if(mapview == null){
mapview = new MapView(getActivity(), getActivity().getString(R.string.map_api_key)); // keep getting the error on this line
mapview_is_active = true;
}
mapview.setClickable(true);
mapview.setBuiltInZoomControls(true);
mapview.setSatellite(true); // Satellite View
mapview.setStreetView(true); // Street View
mapview.setTraffic(true); // Traffic View
frame.addView(mapview,0);
//frame = (FrameLayout)view.findViewById(R.id.mapContainer);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
if(mapview == null){
Log.d(TAG, "mapview is null here");
mapview = new MapView(getActivity(), getActivity().getString(R.string.map_api_key));
}
if(frame.getChildAt(0) == null){
Log.d(TAG, "mapview is null here in container");
frame.addView(mapview);
}
MapController controller = mapview.getController();
double lat = Double.parseDouble(longitude);
double lon = Double.parseDouble(latitude);
GeoPoint geopoint = new GeoPoint((int)(lat * 1E6), (int)(lon * 1E6));
mapview.invalidate();
List<Overlay> mapOverlays = mapview.getOverlays();
Drawable drawable = this.getResources().getDrawable(R.drawable.map_point);
AddMapOverlay add_map_overlay = new AddMapOverlay(drawable, getActivity());
OverlayItem overlayitem = new OverlayItem(geopoint, name, address);
add_map_overlay.addOverlay(overlayitem);
mapOverlays.add(add_map_overlay);
controller.animateTo(geopoint);
controller.setZoom(15);
}
and in my onStop():
#Override
public void onStop(){
super.onStop();
if(frame != null){
frame.removeView(mapview); // i remove the mapview here
}
}
here is my map.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapContainer"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
</FrameLayout>
any help will be greatly appreciated. Thanks

I did this with the below code. I have a MapView instance variable in my FragmentActivity, and then added and removed it in the Fragment itself below (just included the pertinent parts):
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mLayoutMap = (FrameLayout) getActivity().findViewById(R.id.layoutMap);
// create the map view if it's not already there
if (((MyActivity)getActivity()).mMapView == null)
{
((MyActivity)getActivity()).mMapView =
new MapView(getActivity(), getActivity().getString(R.string.map_api_key));
((MyActivity)getActivity()).mMapView.setClickable(true);
}
// add the map view to the frame layout, at the lowest z-index
mLayoutMap.addView(((MyActivity)getActivity()).mMapView, 0);
}
#Override
public void onStop()
{
super.onStop();
if (mLayoutMap.getChildCount() > 0 && mLayoutMap.getChildAt(0) instanceof MapView)
mLayoutMap.removeViewAt(0);
}
mLayoutMap is just a FrameLayout in my XML.

Usually I did this (use android-support-v4-google_maps as you):
1) Implement to activity interface like IMapActivity with methods of attachMap and detachMap
public class MyMapActivity extends FragmentActivity implements IMapActivity {
private MapView mMapView;
public void attachMap(ViewGroup mapHolder) {
if (mMapView == null) {
mMapView = getLayoutInflater().inflate(R.id.mapview, null);
}
mapHolder.addView(mMapView);
}
public void detachMap(ViewGroup mapHolder) {
mapHolder.removeView(mMapView);
}
}
2) Call that methods when need to attach MapView or detach MapView on Fragment.onDestroyView()
public class MyMapFragment extends Fragment {
private IMapActivity mMapViewHolder;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mMapViewHolder = (IMapActivity) activity;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMapViewHolder.attachMap(getMapHolder());
}
#Override
public void onDestroyView() {
mMapViewHolder.detachMap(getMapHolder());
super.onDestroyView();
}
}
I keep in a separate xml MapView with the some settings. When it is necessary to destroy the Fragment - detach MapView, attach MapView when the Fragment is recreated (you can also update the controller of MapView after the attach). This allows me to reuse fragments MapView.
P.S.
I just have not found any way how to clean MapActivity of links to the MapViev. If there is a way, I would be very grateful.

Try using frame.removeAllViews()

If you are working with navHost, you need to add it programmatically not in XML because it will be duplicated.
val mapFragment = SupportMapFragment.newInstance()
requireActivity().supportFragmentManager
.beginTransaction()
.add(R.id.mapFragment, mapFragment)
.commit()

Related

MapView leaks activity (without location service)

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.

Google Map fragment shows the previous fragment's element

Title can be a bit complicated but the problem is literally as described. I'm trying to use google map fragment within a fragment stack. When the application starts the user will see a button and when the user pushes the button, current view will be replaced with map fragment. Currently, I'm able to replace the fragments but when the map fragment is shown, the view still has the button of previous fragment.Also I'm trying to replace fragments in one of the tabs of my app. Here is my code;
Map Fragment;
public class MapFragment extends Fragment {
MapView mMapView;
private GoogleMap googleMap;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_maps, 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;
try {
googleMap.setMyLocationEnabled(true);
} catch (SecurityException e) {
e.printStackTrace();
}
// 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();
}
}
Home Fragment;
public class HomeFragment extends RootFragment {
public HomeFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Obtain the shared Tracker instance.
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.home_tab, container, false);
Button go = (Button)v.findViewById(R.id.btn_go);
go.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Intent intent = new Intent(getActivity(), MapFragment.class);
//startActivity(intent);
enterNextFragment();
}
});
return v;
}
#Override
public void onResume() {
super.onResume();
}
private void enterNextFragment() {
// Pushing MapView Fragment
Fragment fragment = Fragment.instantiate(this.getContext(), MapFragment.class.getName());
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment_mainLayout, fragment);
ft.commit();
}
}
Map Layout;
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Home Fragment;
<FrameLayout 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"
tools:context=".HomeFragment"
android:background="#android:color/white"
android:id="#+id/fragment_mainLayout">
<!-- TODO: Update blank fragment layout -->
<android.support.v7.widget.AppCompatButton
android:id="#+id/btn_go"
android:layout_width="fill_parent"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp"
android:layout_height="wrap_content"
android:layout_marginTop="170dp"
android:text="GO"/>
</FrameLayout>
Often you will want one Fragment to communicate with another, for
example to change the content based on a user event. All
Fragment-to-Fragment communication is done through the associated
Activity. Two Fragments should never communicate directly.
Your fragments shouldn't communicate directly according to the documentation.
If you've managed to communicate with your fragments considering the documentation. Then you can use this simple method to be able to change and reuse any fragment.
In your host activity :
private void changeFragment(Fragment frag, boolean saveInBackstack) {
String backStateName = ((Object) frag).getClass().getName();
try {
FragmentManager manager = getSupportFragmentManager();
if (manager.findFragmentByTag(backStateName) == null) {
//fragment not in back stack, create it.
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.container, frag, backStateName);
if (saveInBackstack) {
Log.d(TAG, "Change Fragment: addToBackTack " + backStateName);
transaction.addToBackStack(backStateName);
} else {
Log.d(TAG, "Change Fragment: NO addToBackTack");
}
transaction.commit();
} else {
manager.popBackStack();
}
} catch (IllegalStateException exception) {
Log.w(TAG, "Unable to commit fragment, could be activity as been killed in
}
}
just add android:background="#android:color/white" on the main view of the map_layout xml file
Here is the solution;
Home Fragment;
public class HomeFragment extends RootFragment {
private LocationManager locationManager;
public HomeFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Obtain the shared Tracker instance.
locationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.home_tab, container, false);
Button go = (Button) v.findViewById(R.id.btn_go);
go.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), MapFragment.class);
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
//Toast.makeText(getContext(), "GPS is Enabled in your devide", Toast.LENGTH_SHORT).show();
startActivity(intent);
}
}
});
return v;
}
#Override
public void onResume() {
super.onResume();
}
}
MapFragment (it is now fragmentActivity not fragment);
public class MapFragment extends FragmentActivity implements OnMapReadyCallback,
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
LocationListener {
MapView mMapView;
private GoogleMap googleMap;
// private GoogleMap map;
private LocationRequest mLocationRequest;
private GoogleApiClient mGoogleApiClient;
private Location mLastLocation;
private Marker marker;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
mMapView = (MapView) findViewById(R.id.mapView);
mMapView.onCreate(savedInstanceState);
mMapView.onResume(); // needed to get the map to display immediately
try {
MapsInitializer.initialize(this.getApplicationContext());
} catch (Exception e) {
e.printStackTrace();
}
mMapView.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap mMap) {
googleMap = mMap;
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
Location location = null;
try {
googleMap.setMyLocationEnabled(true);
location = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
} catch (SecurityException e) {
e.printStackTrace();
}
if (location != null) {
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 13));
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(location.getLatitude(), location.getLongitude())) // Sets the center of the map to location user
.zoom(17) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(40) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
}
}

Google maps in an actionbarsherlock tab

I am trying to get google maps v2 working in my app. I have seen several examples showing how you can open up SupportMapFragment inside an activity. The idea being that your activity will call setContentView(R.layout.map_layout); where map_layout.xml links to the fragment with the lines:
android:name="com.google.android.gms.maps.SupportMapFragment"
xmlns:map="http://schemas.android.com/apk/res-auto"
The "name=" line effectively says that "this layout is to be controlled by a fragment of type 'SupportMapFragment'".
My complication is that I am attempting to get the map to appear in an activity with tabs (implemented with actionbarsherlock). This means that whatever fragment corresponds to a tab selection must implement a TabListener. But SupportMapFragment doesn't. So now presumably I need to create a new fragment like so:
public class MyMapFragmentWithTabListener extends SupportMapFragment implements TabListener
{
But now I have got all confused about how to write the contents of MapFragmentWithTabListener in particular onCreateView... should I be inflating some layout? Surely I can't be inflating exactly the same map_layout.xml from the examples because that already declares that it is controlled by SupportMapFragment, whereas in this implementation it should be controlled by MyMapFragmentWithTabListener - do I need a slightly different xml file to inflate (if so, what should it look like?) - or should I be creating my view programatically?
I've done this in quite a few applications now. Instead of extending SupportMapFragment, you just create your own MapFragment. You can have your own layout, with a MapView view inside of it. The key is to route the lifecycle events of the Fragment to the MapView, and bobs your uncle.
Heres some example code:
MapFragment
package com.example.testapplication;
import java.util.concurrent.TimeUnit;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
public class TestMapFragment extends Fragment {
private MapView mMapView;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.home_fragment, container, false);
mMapView = (MapView) view.findViewById(R.id.mapview);
// inflat and return the layout
mMapView.onCreate(savedInstanceState);
mMapView.onResume();// needed to get the map to display immediately
try {
MapsInitializer.initialize(getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
GoogleMap googleMap = mMapView.getMap();
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
/*
* Using a mapview in a fragment requires you to 'route'
* the lifecycle events of the fragment to the mapview
*/
#Override
public void onResume() {
super.onResume();
if (null != mMapView)
mMapView.onResume();
}
#Override
public void onPause() {
super.onPause();
if (null != mMapView)
mMapView.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
if (null != mMapView)
mMapView.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (null != mMapView)
mMapView.onSaveInstanceState(outState);
}
#Override
public void onLowMemory() {
super.onLowMemory();
if (null != mMapView)
mMapView.onLowMemory();
}
}
And the layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.google.android.gms.maps.MapView
android:id="#+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:uiZoomControls="false" />
</RelativeLayout>
You can used this way.
public class NewActivity extends FragmentActivity {
private GoogleMap mMap;
SupportMapFragment mapFragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activit);
mapFragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
if(isGooglePlayServicesIsInstalled(mContext)){
mMap = mapFragment.getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
mMap.getUiSettings().setCompassEnabled(true);
mMap.getUiSettings().setZoomControlsEnabled(true);
}else{
//display any toast message
//Global.Toast("Please First install Google Maps");
}
public static boolean isGooglePlayServicesIsInstalled(Context context){
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (resultCode == ConnectionResult.SUCCESS)
return true;
else
return false;
}
Please check it out all the permission,api key and require all the thing.if you getting any error log then put as a comment.

Android Google Maps : Map View initialization

I am trying to display a simple map in my Android application, using the MapView class.
I use the following onCreate method in my activity :
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
MapsInitializer.initialize(this);
} catch (GooglePlayServicesNotAvailableException e) {
Log.e("Address Map", "Could not initialize google play", e);
}
MapView mapView = new MapView(this);
CameraUpdate camPos = CameraUpdateFactory.newLatLng(new LatLng(11.562276,104.920292));
mapView.getMap().moveCamera(camPos);
setContentView(mapView);
}
I have a NullPointerException, because the method mapView.getMap() returns null.
Don't understand why, Google play services are apparently present and initialized.
Could not get MapView to work, I finally ended using the class SupportMapFragment.
For those that it may help, here is the complete code of my activity :
public class AddressMap extends android.support.v4.app.FragmentActivity {
private final static int FRAGMENT_ID = 0x101;
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
SupportMapFragment fragment = SupportMapFragment.newInstance();
layout.setId(0x101);
{
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(FRAGMENT_ID, fragment);
fragmentTransaction.commit();
}
setContentView(layout);
}
#Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
}
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(FRAGMENT_ID)).getMap();
if (mMap != null) {
mMap.moveCamera(
CameraUpdateFactory.newLatLngZoom(new LatLng(11.562276, 104.920292), 10));
}
}
}
}
Also, mapView.onCreate() must be called.
You are trying to access Google Maps Android v1 Api which is now deprecated, please use Google Map Android Api v2
I used it like this and it worked, this is with the new api:
compile 'com.google.android.gms:play-services-maps:10.2.6'
public class MapViewFragment extends Fragment{
public static final String TAG = MapViewFragment.class.getSimpleName();
MapView mapView;
GoogleMap map;
static MapViewFragment instance;
public static MapViewFragment newInstance(){
if(instance == null){
instance = new MapViewFragment();
return instance;
}else{
return instance;
}
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Log.i(TAG, "onCreateView: " );
View view = inflater.inflate(R.layout.map_view_fragment, container, false);
mapView = (MapView) view.findViewById(R.id.mvMap);
if(mapView != null){
mapView.onCreate(null); //Don't forget to call onCreate after get the mapView!
mapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
//Do what you want with the map!!
}
});
MapsInitializer.initialize(this.getActivity());
}else{
Log.i(TAG, "onCreateView: mapView is null");
}
return view;
}
#Override
public void onResume() {
mapView.onResume();
super.onResume();
}
#Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
}
And here is my layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.gms.maps.MapView
android:id="#+id/mvMap"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.google.android.gms.maps.MapView>
</LinearLayout>

Initialize MapFragment programmatically with Maps API v2

I'm trying to add a MapFragment to my current Fragment. The use of nested fragments is restricted to FragmentTransactions, you can't use the xml tag in your layout.
Also, I want it to be added to the main Fragment when the user presses a button. So, I'm creating the MapFragment programmatically with getInstance() when the user presses that button and adding it to the proper place. It is shown correctly, so far so good.
The problem is that after attaching the MapFragment I need to get a reference to GoogleMap to place a Marker, but the getMap() method returns null (as the fragment's onCreateView() hasn't been called yet).
I looked at the demo example code and I found the solution they use is initializing the MapFragment in onCreate() and getting the reference to GoogleMap in onResume(), after onCreateView() has been called.
I need to get the reference to GoogleMap right after the MapFragment initialization, because I want the users to be able to show or hide the map with a button. I know a possible solution would be to create the Map at the start as said above and just set it's visibility gone, but I want the map to be off by default so it doesn't take the user's bandwidth if they don't explicitly asked for it.
I tried with the MapsInitializer, but doesn't work either. I'm kind of stuck. Any ideas?
Here is my testing code so far:
public class ParadaInfoFragment extends BaseDBFragment {
// BaseDBFragment is just a SherlockFragment with custom utility methods.
private static final String MAP_FRAGMENT_TAG = "map";
private GoogleMap mMap;
private SupportMapFragment mMapFragment;
private TextView mToggleMapa;
private boolean isMapVisible = false;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_parada_info, container, false);
mToggleMapa = (TextView) v.findViewById(R.id.parada_info_map_button);
return v;
}
#Override
public void onStart() {
super.onStart();
mToggleMapa.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!isMapVisible) {
openMap();
} else {
closeMap();
}
isMapVisible = !isMapVisible;
}
});
}
private void openMap() {
// Creates initial configuration for the map
GoogleMapOptions options = new GoogleMapOptions().camera(CameraPosition.fromLatLngZoom(new LatLng(37.4005502611301, -5.98233461380005), 16))
.compassEnabled(false).mapType(GoogleMap.MAP_TYPE_NORMAL).rotateGesturesEnabled(false).scrollGesturesEnabled(false).tiltGesturesEnabled(false)
.zoomControlsEnabled(false).zoomGesturesEnabled(false);
// Modified from the sample code:
// It isn't possible to set a fragment's id programmatically so we set a
// tag instead and search for it using that.
mMapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentByTag(MAP_FRAGMENT_TAG);
// We only create a fragment if it doesn't already exist.
if (mMapFragment == null) {
// To programmatically add the map, we first create a
// SupportMapFragment.
mMapFragment = SupportMapFragment.newInstance(options);
// Then we add it using a FragmentTransaction.
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.parada_info_map_container, mMapFragment, MAP_FRAGMENT_TAG);
fragmentTransaction.commit();
}
// We can't be guaranteed that the map is available because Google Play
// services might not be available.
setUpMapIfNeeded(); //XXX Here, getMap() returns null so the Marker can't be added
// The map is shown with the previous options.
}
private void closeMap() {
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
fragmentTransaction.remove(mMapFragment);
fragmentTransaction.commit();
}
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the
// map.
if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
mMap = mMapFragment.getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
mMap.addMarker(new MarkerOptions().position(new LatLng(37.4005502611301, -5.98233461380005)).title("Marker"));
}
}
}
}
Thanks
The good AnderWebs gave me an answer in Google+ but he is too laz.... emm busy to write it here again, so here is the short version:
Extend the MapFragment class and override the onCreateView() method. After this method is done we can get a non-null reference to que GoogleMap object.
This is my particular solution:
public class MiniMapFragment extends SupportMapFragment {
private LatLng mPosFija;
public MiniMapFragment() {
super();
}
public static MiniMapFragment newInstance(LatLng posicion){
MiniMapFragment frag = new MiniMapFragment();
frag.mPosFija = posicion;
return frag;
}
#Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
View v = super.onCreateView(arg0, arg1, arg2);
initMap();
return v;
}
private void initMap(){
UiSettings settings = getMap().getUiSettings();
settings.setAllGesturesEnabled(false);
settings.setMyLocationButtonEnabled(false);
getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(mPosFija,16));
getMap().addMarker(new MarkerOptions().position(mPosFija).icon(BitmapDescriptorFactory.fromResource(R.drawable.marker)));
}
}
Now in the previous Fragment class I do
mMapFragment = MiniMapFragment.newInstance(new LatLng(37.4005502611301, -5.98233461380005));
Maybe it's not perfect yet, because the screen blinks when showing the map. But not sure if the problem is because of this or something else.
Thanks, found this very helpful. Am posting my slightly modified solution, as it was cleaner for me to tell the parent Fragment when the map was ready. This method also works with a saveInstanceState / restoreInstanceState cycle.
public class CustomMapFragment extends SupportMapFragment {
private static final String LOG_TAG = "CustomMapFragment";
public CustomMapFragment() {
super();
}
public static CustomMapFragment newInstance() {
CustomMapFragment fragment = new CustomMapFragment();
return fragment;
}
#Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
View v = super.onCreateView(arg0, arg1, arg2);
Fragment fragment = getParentFragment();
if (fragment != null && fragment instanceof OnMapReadyListener) {
((OnMapReadyListener) fragment).onMapReady();
}
return v;
}
/**
* Listener interface to tell when the map is ready
*/
public static interface OnMapReadyListener {
void onMapReady();
}
}
To use as a nested Fragment:-
public class ParentFragment extends Fragment implements OnMapReadyListener {
...
mMapFragment = CustomMapFragment.newInstance();
getChildFragmentManager().beginTransaction().replace(R.id.mapContainer, mMapFragment).commit();
#Override
public void onMapReady() {
mMap = mMapFragment.getMap();
}
...
}
Hope it helps someone.
Here's my solution to this, I took inspiration from the code previously posted and cleaned it up. I also added the static methods with and without the GoogleMapOptions parameters.
public class GoogleMapFragment extends SupportMapFragment {
private static final String SUPPORT_MAP_BUNDLE_KEY = "MapOptions";
public static interface OnGoogleMapFragmentListener {
void onMapReady(GoogleMap map);
}
public static GoogleMapFragment newInstance() {
return new GoogleMapFragment();
}
public static GoogleMapFragment newInstance(GoogleMapOptions options) {
Bundle arguments = new Bundle();
arguments.putParcelable(SUPPORT_MAP_BUNDLE_KEY, options);
GoogleMapFragment fragment = new GoogleMapFragment();
fragment.setArguments(arguments);
return fragment;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (OnGoogleMapFragmentListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().getClass().getName() + " must implement OnGoogleMapFragmentListener");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (mCallback != null) {
mCallback.onMapReady(getMap());
}
return view;
}
private OnGoogleMapFragmentListener mCallback;
}
The usage pattern is as follows:
public class MyMapActivity implements OnGoogleMapFragmentListener {
...
#Override
public void onMapReady(GoogleMap map) {
mUIGoogleMap = map;
...
}
...
private GoogleMap mUIGoogleMap;
}
No need to cutomize SupportMapFragment you can do this directly by using following piece of code,
FragmentManager fm = getSupportFragmentManager(); // getChildFragmentManager inside fragments.
CameraPosition cp = new CameraPosition.Builder()
.target(initialLatLng) // your initial co-ordinates here. like, LatLng initialLatLng
.zoom(zoom_level)
.build();
SupportMapFragment mapFragment = SupportMapFragment.newInstance(new GoogleMapOptions().camera(cp));
fm.beginTransaction().replace(R.id.rl_map, mapFragment).commit();
Add this piece of code for layout
<RelativeLayout
android:id="#+id/rl_map"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
This will load GoogleMap at particular Location directly i.e, initialLatLng.

Categories

Resources