Best way to put a map (google maps v2) inside a fragment - android

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`
}

Related

How to fix black screen when replacing SupportMapFragment

I'm creating a simple application using Mapbox (using mapbox-android-sdk:7.1.0 ) in a fragment.
I have an activity which is composed of a FrameLayout (containing the Fragments) and a button.
At the beginning, the Fragment1 (containing the map) is displayed in the FrameLayout. When the user clicks on the button, the Fragment1 is replaced by the Fragment2 (containing a TextView).
During the transition there is a short black screen.
When I use Mapbox in an activity I don't have any problem, It seems to happen when the method onDestroyView() is called.
MainActivity.java :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Load Fragment1 containing the map
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new Fragment1()).commit();
AppCompatButton button = findViewById(R.id.btn_changeFragment);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Load Fragment2 containing a TextView
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new Fragment2()).addToBackStack(null).commit();
}
});
}
}
Fragment1.java :
public class Fragment1 extends SupportMapFragment {
private MapView mapView;
public Fragment1(){}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(getContext(), getString(R.string.mapbox_token));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_1, container, false);
mapView = view.findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(#NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.LIGHT, new Style.OnStyleLoaded() {
#Override
public void onStyleLoaded(#NonNull Style style) {
// Configure the map
}
});
}
});
return view;
}
#Override
public void onStart() {
super.onStart();
mapView.onStart();
}
#Override
public void onResume() {
super.onResume();
mapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mapView.onPause();
}
#Override
public void onStop() {
super.onStop();
mapView.onStop();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
public void onDestroyView() {
super.onDestroyView();
mapView.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}
Using this Attribute app:mapbox_renderTextureMode="true" while loading mapview inside the fragment did the trick for me.
This is a known issue with how MapFragment's behave: https://github.com/mapbox/mapbox-gl-native/issues/9570. Per the ticket, there are currently two possible ways to resolve this issue in your app.
Either:
Use a bitmap of the map in an ImageView while transitioning instead of a MapView. You can then make the MapView visible as part of the OnMapReady callback in your activity.
Or:
Use a TextureView implementation instead. This can be enabled via MapboxMapOptions or .xml attributes. You should also keep in mind that this solution may lead to performance issues down the road.
I tried the first solution proposed by #riastrad but I didnĀ“t find how to make a bitmap of the mapView ( I always had a white screen with the mapbox logo at the bottom).
Anyway, I tried to use setAlpha to change the opacity of the mapView, and it works. I don't realy know why it doesn't work with setVisibility(View.INVISIBLE).
So before to load the Fragment2, I call a method from Fragment1 to set the opacity of the mapView to 0.
MainActivity.java
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if(currentFragment instanceof Fragment1){
// Set the opacity of the mapView to 0
((Fragment1) currentFragment).transition();
// Load Fragment2 containing a TextView
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new Fragment2()).addToBackStack(null).commit();
}else {
getSupportFragmentManager().popBackStack();
}
}
});
Fragment1.java
public void transition(){
mapView.setAlpha(0f);
}

how do i implement doinbackground callback method

my application started crashing after the implementation of google maps into a separate tab in my android application. i have looked through all other classes and logcat and deemed those classes to lightweight to be causing this issue.
here is my maps class
public class FragmentShouts_Maps extends Fragment implements OnMapReadyCallback {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Get the view from fragment shouts.xml
View view = inflater.inflate(R.layout.fragmentshouts_maps, container, false);
SupportMapFragment fragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.maps);
if (fragment!= null) {
fragment.getMapAsync(this);
}
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
#Override
public void onMapReady (GoogleMap googleMap) {
}
}
i've noticed when i use an emulator with a lower dpi and without google play my application will make it to this tab but will be stopped and ended. on newer api(23) with google play services it wont even make it that far, once i swipe away from the home tab, it becomes unresponsive and ends.
i have no error codes so should work once i implement the doinbackground method i believe i just dont understand how to implement this?
Don't know whats wrong with your code but I implemented google maps in my app and it works just fine. See if this is the kind of doInBackground implementation you are looking for. Hope this helps.
public class MapActivity extends Activity implements OnMapReadyCallback{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
MapFragment mapFragment = (MapFragment)getFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(final GoogleMap map){
// Your Code
AsyncTask<String,Void,String> asyncTask = new AsyncTask<String,Void,String>(){
#Override
protected String doInBackground(String... params) {
// Do Something
}
#Override
protected void onPostExecute(String json) {
// Do Something
}
};
asyncTask.execute(url);
}
}

Loading Google Maps is too slow in Android Application

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();
}

GoogleMap - animateCamera() doing nothing after Fragment resume

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().

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.

Categories

Resources