Android Mapview in Fragment

android MapView in Fragment

From Josh Holtz's example on GitHub:

You should add MapView in your Layout like

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

and implement your Fragment like

public class SomeFragment extends Fragment {

MapView mapView;
GoogleMap map;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.some_layout, container, false);

// Gets the MapView from the XML layout and creates it
mapView = (MapView) v.findViewById(R.id.mapview);
mapView.onCreate(savedInstanceState);

// Gets to GoogleMap from the MapView and does initialization stuff
map = mapView.getMap();
map.getUiSettings().setMyLocationButtonEnabled(false);
map.setMyLocationEnabled(true);

// Needs to call MapsInitializer before doing any CameraUpdateFactory calls
try {
MapsInitializer.initialize(this.getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}

// Updates the location and zoom of the MapView
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(new LatLng(43.1, -87.9), 10);
map.animateCamera(cameraUpdate);

return v;
}

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

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

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

}

how do i solve integrating google map in fragment in android

Please follow below step to finish your task. Just need to create 3 files

(1) Create XML layout for map inside your fragment layout fragment_map.xml

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />

(2) Create Fragment to load MAP MapFragment.Java

public class MapFragment extends Fragment implements OnMapReadyCallback {

private GoogleMap mMap;

public MapFragment() {
// Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_map, container, false);
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

// Obtain the SupportMapFragment and get notified when the map is ready to be used.
if(getActivity()!=null) {
SupportMapFragment mapFragment = (SupportMapFragment) getActivity().getSupportFragmentManager()
.findFragmentById(R.id.map);
if (mapFragment != null) {
mapFragment.getMapAsync(this);
}
}
}

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

//Do your stuff here
}
}

(3) Create Activity to load MAP fragment MapsActivity.java

public class MapsActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.content,new MapFragment());
fragmentTransaction.commit();
}

}

For MAP key you need to follow same step you have done in your project. Hope this step will help you.

Sample Image

Inside Gradle please use below gradle

implementation 'com.google.android.gms:play-services-maps:15.0.1'

AndroidManifest.xml define below things.

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

Inside Application Tag.

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR MAP KEY" />

Here Maps MapView does not work in Fragment

My own solution and it worked very well. Removed MapView completely

Just added FrameLayout in layout xml of Fragment

 <FrameLayout
android:id="@+id/simpleFrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true" />

Now, in onCreateView(...) created MapFragment dynamically and added in FrameLayout and init

    MapFragment mapFragment = new MapFragment();
FragmentManager fm = mActivity.getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.simpleFrameLayout, mapFragment).commit();

mapFragment.init(mActivity, new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted(OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {

// retrieve a reference of the map from the map fragment
map = mapFragment.getMap();
} else {
logger.error("ERROR: Cannot initialize Map Fragment: " + error.getStackTrace());
}
}
});

Mapview in separate fragment android

You need to have the supportMapFragment in your xml file and get the reference of mapFragment into your MainFragment. The below code explains, How I accomplished the MapView inside Fragment. Write the below XML code in the mapView.xml and then Use the Fragment OnCreateView to get references.

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainMap"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="200dp"
/>

public class MapInFragment extends Fragment implements OnMapReadyCallback {
private View rootView;
private GoogleMap mMap;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.mapView, container, false);

try{
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.mainMap);
mapFragment.getMapAsync(MapInFragment.this);
}catch (Exception e){
e.printStackTrace();
}

return rootView;
}

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

// Add a marker in Sydney and move the camera
LatLng croplatlng = new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude));
mMap.addMarker(new MarkerOptions().position(croplatlng).title(crop + "Field"));

//mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(croplatlng,16));

CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(croplatlng , 16);
//mMap.addMarker(new MarkerOptions().position(location).title(""));
//mMap.moveCamera(CameraUpdateFactory.newLatLng(currentLocation ));
mMap.animateCamera(cameraUpdate,2000,null);
}catch (Exception e){
e.printStackTrace();
}

}

public void onDestroyView()
{
try {
Fragment fragment = (getChildFragmentManager().findFragmentById(R.id.mainMap));
if (fragment != null) {
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.remove(fragment);
ft.commit();
}
}catch (Exception e){
e.printStackTrace();
}
super.onDestroyView();
}

}

How to put Google Maps V2 on a Fragment using ViewPager

By using this code we can setup MapView anywhere, inside any ViewPager or Fragment or Activity.

In the latest update of Google for Maps, only MapView is supported for fragments. MapFragment & SupportMapFragment didn't work for me.

Setting up the layout for showing the map in the file location_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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" />

</RelativeLayout>

Now, we setup the Java class for showing the map in the file MapViewFragment.java:

public class MapViewFragment extends Fragment {

MapView mMapView;
private GoogleMap googleMap;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.location_fragment, 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 showing a move to my location button
googleMap.setMyLocationEnabled(true);

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

Finally you need to get the API Key for your app by registering your app at Google Cloud Console. Register your app as Native Android App.

Mapbox SDK in Android Fragment

Problem is that there is no setted access token. Access token should be set before inflating. That's all.

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;

public class MapFragment extends Fragment {

private MapView mapView;

public MapFragment(){

}

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

Mapbox.getInstance(getContext().getApplicationContext(),"access_token");
View view = inflater.inflate(R.layout.fragment_map,container,false);

mapView = (MapView) view.findViewById(R.id.mapview);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
mapboxMap.setStyle(new Style.Builder().fromUrl("style_url"));
}
});

return view;
}

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

@Override
public void onPause() {
super.onPause();
mapView.onPause();
}

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

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

@Override
public void onDestroyView() {
super.onDestroyView();

mapView.onDestroy();

}

}

Retrieve the current Location in fragment with MapView in kotlin?

Use the fused location provider to retrieve the device's last known location.

private lateinit var fusedLocationClient: FusedLocationProviderClient

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {

// ...

fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}

To request the last known location and handle the response:

fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
// Got last known location. In some rare situations this can be null.
}

This returns a Task that you can use to get a Location object with the latitude and longitude coordinates of a geographic location.

For more details:
https://developer.android.com/training/location/request-updates

Update:
Complete implementation

@SuppressLint("MissingPermission")
private fun getDeviceLocation() {
/*
* Get the best and most recent location of the device, which may be null in rare
* cases when a location is not available.
*/
try {
if (locationPermissionGranted) {
val locationResult = fusedLocationProviderClient.lastLocation
locationResult.addOnCompleteListener(requireActivity()) { task ->

if (task.isSuccessful) {

// Set the map's camera position to the current location of the device.
lastKnownLocation = task.result
if (lastKnownLocation != null) {
Timber.d("last known location $lastKnownLocation")

map.moveCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(
lastKnownLocation!!.latitude,
lastKnownLocation!!.longitude
), DEFAULT_ZOOM.toFloat()
)
)
} else {
Timber.e( "Exception: %s", task.exception)
map.moveCamera(
CameraUpdateFactory
.newLatLngZoom(defaultLocation, DEFAULT_ZOOM.toFloat())
)
map.uiSettings?.isMyLocationButtonEnabled = false
}
}
}
}
} catch (e: SecurityException) {
Timber.e("Exception: %s", e.message)
}
}

Update 2: Location updates

This code is from the official documentation, implemented for an activity. Should work well with fragments as well. Note: I have not tested this.

Call requestLocationUpdates(), passing it your instance of the locationRequest object, and a locationCallback. Define a startLocationUpdates() method as shown in the following code sample:

override fun onResume() {
super.onResume()
if (requestingLocationUpdates) startLocationUpdates()
}

private fun startLocationUpdates() {
fusedLocationClient.requestLocationUpdates(locationRequest,
locationCallback,
Looper.getMainLooper())
}

The fused location provider invokes the LocationCallback.onLocationResult() callback method. The incoming argument contains a list Location object containing the location's latitude and longitude. The following snippet shows how to implement the LocationCallback interface and define the method, then get the timestamp of the location update and display the latitude, longitude and timestamp on your app's user interface:

private lateinit var locationCallback: LocationCallback

// ...

override fun onCreate(savedInstanceState: Bundle?) {
// ...

locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations){
// Update UI with location data
// ...
}
}
}
}

To stop location updates, call removeLocationUpdates(), passing it a locationCallback, as shown in the following code sample:

override fun onPause() {
super.onPause()
stopLocationUpdates()
}

private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}

A change to the device's configuration, such as a change in screen orientation or language, can cause the current activity to be destroyed. Your app must therefore store any information it needs to recreate the activity. One way to do this is via an instance state stored in a Bundle object.

The following code sample shows how to use the activity's onSaveInstanceState() callback to save the instance state:

override fun onSaveInstanceState(outState: Bundle?) {
outState?.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, requestingLocationUpdates)
super.onSaveInstanceState(outState)
}

Define an updateValuesFromBundle() method to restore the saved values from the previous instance of the activity, if they're available. Call the method from the activity's onCreate() method, as shown in the following code sample:

override fun onCreate(savedInstanceState: Bundle?) {
// ...
updateValuesFromBundle(savedInstanceState)
}

private fun updateValuesFromBundle(savedInstanceState: Bundle?) {
savedInstanceState ?: return

// Update the value of requestingLocationUpdates from the Bundle.
if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
requestingLocationUpdates = savedInstanceState.getBoolean(
REQUESTING_LOCATION_UPDATES_KEY)
}

// ...

// Update UI to match restored state
updateUI()
}

For a more persistent storage, you can store the user's preferences in your app's SharedPreferences. Set the shared preference in your activity's onPause() method, and retrieve the preference in onResume()



Related Topics



Leave a reply



Submit