I want to have MapView
inside my Fragment
This is my FragmentLayout
xml file
Makes sure you are overriding or calling OnDestroyView as well as OnDestroy. Something like ...
public override void OnDestroy()
{
base.OnDestroy();
MapView.OnDestroy();
OnDestroyView();
}
public override void OnDestroyView()
{
base.OnDestroyView();
mapReadyCallbackList.Clear();
}
In case somebody is looking for a Kotlin version of MapView Fragment ;)
class MapViewKotlinFragment : Fragment(), OnMapReadyCallback {
private var mMap: MapView? = null
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
mMap?.onSaveInstanceState(outState)
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_map, container, false)
mMap = view?.findViewById(R.id.mapViewPlaces) as MapView
mMap?.onCreate(savedInstanceState)
mMap?.getMapAsync(this)
return view
}
override fun onResume() {
super.onResume()
mMap?.onResume()
}
override fun onPause() {
super.onPause()
mMap?.onPause()
}
override fun onStart() {
super.onStart()
mMap?.onStart()
}
override fun onStop() {
super.onStop()
mMap?.onStop()
}
override fun onDestroy() {
super.onDestroy()
mMap?.onDestroy()
}
override fun onLowMemory() {
super.onLowMemory()
mMap?.onLowMemory()
}
override fun onMapReady(googleMap: GoogleMap) {
googleMap.addMarker(MarkerOptions().position(LatLng(0.0, 0.0)).title("Marker"))
}
Old post, but might help someone who is looking to implement maps inside the fragment. Below is the detailed example using kotlin:
First, add the permission in AndroidManifest.xml file:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Then under the application tag add meta-data in AndroidManifest.xml file:
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/map_api_key"/>
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
*To get api link visit: https://developers.google.com/maps/documentation/android-sdk/get-api-key
Implement play services in build.gradle file:
implementation "com.google.android.gms:play-services-location:15.0.1"
implementation "com.google.android.gms:play-services-places:15.0.1"
implementation 'com.google.android.libraries.places:places:2.3.0'
Add pulgin: apply plugin: 'com.google.gms.google-services'
Add maps fragment inside your fragment.xml file:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
In your fragment.kt file, implement OnMapReadyCallback. Initialize the google map using getMapAsync(). Below code will let you integrate the google map and point it to your current location.
package "Your_Package_Name"
import android.Manifest
import android.content.ContentValues.TAG
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.core.content.ContextCompat
import androidx.appcompat.app.AlertDialog
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import com.example.utilitybox.R
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.maps.*
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.libraries.places.api.model.Place
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest
import com.google.android.libraries.places.api.net.PlacesClient
class FragmentHome : Fragment(), OnMapReadyCallback {
private var v:View?=null
private var locationPermissionGranted = false
private var googleMap: GoogleMap?=null
private var fusedLocationProviderClient: FusedLocationProviderClient?=null
private val defaultLocation = LatLng(-33.8523341, 151.2106085)
private var cameraPosition: CameraPosition? = null
private var placesClient: PlacesClient?=null
private var lastKnownLocation: Location? = null
private var likelyPlaceNames: Array<String?> = arrayOfNulls(0)
private var likelyPlaceAddresses: Array<String?> = arrayOfNulls(0)
private var likelyPlaceAttributions: Array<List<*>?> = arrayOfNulls(0)
private var likelyPlaceLatLngs: Array<LatLng?> = arrayOfNulls(0)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (savedInstanceState != null) {
lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
cameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION)
}
v = inflater.inflate(R.layout.fragment_fragment_home, container, false)
if(getString(R.string.map_api_key).isEmpty()){
Toast.makeText(activity, "Add your own API key in MapWithMarker/app/secure.properties as MAPS_API_KEY=YOUR_API_KEY", Toast.LENGTH_LONG).show()
}
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment
mapFragment?.getMapAsync(this)
getLocationPermission()
return v
}
override fun onSaveInstanceState(outState: Bundle) {
googleMap?.let { map ->
outState.putParcelable(KEY_CAMERA_POSITION, map.cameraPosition)
outState.putParcelable(KEY_LOCATION, lastKnownLocation)
}
super.onSaveInstanceState(outState)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.option_get_place) {
showCurrentPlace()
}
return true
}
override fun onMapReady(googleMap: GoogleMap) {
this.googleMap = googleMap
this.googleMap?.setInfoWindowAdapter(object : GoogleMap.InfoWindowAdapter {
override fun getInfoWindow(arg0: Marker): View? {
return null
}
override fun getInfoContents(marker: Marker): View {
val infoWindow = layoutInflater.inflate(R.layout.custom_info_contents,
v?.findViewById(R.id.map), false)
val title = infoWindow.findViewById<TextView>(R.id.title)
title.text = marker.title
val snippet = infoWindow.findViewById<TextView>(R.id.snippet)
snippet.text = marker.snippet
return infoWindow
}
})
getLocationPermission()
updateLocationUI()
getDeviceLocation()
}
private fun getLocationPermission() {
if (ContextCompat.checkSelfPermission(this.requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationPermissionGranted = true
} else {
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION)
}
}
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
locationPermissionGranted = false
when (requestCode) {
PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION -> {
if (grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
locationPermissionGranted = true
}
}
}
updateLocationUI()
}
private fun showCurrentPlace() {
if (googleMap == null) {
return
}
if (locationPermissionGranted) {
val placeFields = listOf(Place.Field.NAME, Place.Field.ADDRESS, Place.Field.LAT_LNG)
val request = FindCurrentPlaceRequest.newInstance(placeFields)
val placeResult = placesClient?.findCurrentPlace(request)
placeResult?.addOnCompleteListener { task ->
if (task.isSuccessful && task.result != null) {
val likelyPlaces = task.result
val count = if (likelyPlaces != null && likelyPlaces.placeLikelihoods.size < M_MAX_ENTRIES) {
likelyPlaces.placeLikelihoods.size
} else {
M_MAX_ENTRIES
}
var i = 0
likelyPlaceNames = arrayOfNulls(count)
likelyPlaceAddresses = arrayOfNulls(count)
likelyPlaceAttributions = arrayOfNulls<List<*>?>(count)
likelyPlaceLatLngs = arrayOfNulls(count)
for (placeLikelihood in likelyPlaces?.placeLikelihoods ?: emptyList()) {
likelyPlaceNames[i] = placeLikelihood.place.name
likelyPlaceAddresses[i] = placeLikelihood.place.address
likelyPlaceAttributions[i] = placeLikelihood.place.attributions
likelyPlaceLatLngs[i] = placeLikelihood.place.latLng
i++
if (i > count - 1) {
break
}
}
openPlacesDialog()
} else {
Log.e(TAG, "Exception: %s", task.exception)
}
}
} else {
Log.i(TAG, "The user did not grant location permission.")
googleMap?.addMarker(MarkerOptions()
.title(getString(R.string.default_info_title))
.position(defaultLocation)
.snippet(getString(R.string.default_info_snippet)))
getLocationPermission()
}
}
private fun updateLocationUI() {
if (googleMap == null) {
return
}
try {
if (locationPermissionGranted) {
googleMap?.isMyLocationEnabled = true
googleMap?.uiSettings?.isMyLocationButtonEnabled = true
} else {
googleMap?.isMyLocationEnabled = false
googleMap?.uiSettings?.isMyLocationButtonEnabled = false
lastKnownLocation = null
getLocationPermission()
}
} catch (e: SecurityException) {
Log.e("Exception: %s", e.message, e)
}
}
private fun getDeviceLocation() {
try {
if (locationPermissionGranted) {
val locationResult = fusedLocationProviderClient?.lastLocation
if (locationResult != null) {
locationResult.addOnCompleteListener(this.requireActivity()) { task ->
if (task.isSuccessful) {
// Set the map's camera position to the current location of the device.
lastKnownLocation = task.result
if (lastKnownLocation != null) {
googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(
LatLng(lastKnownLocation!!.latitude,
lastKnownLocation!!.longitude), DEFAULT_ZOOM.toFloat()))
}
} else {
Log.d(TAG, "Current location is null. Using defaults.")
Log.e(TAG, "Exception: %s", task.exception)
googleMap?.moveCamera(CameraUpdateFactory
.newLatLngZoom(defaultLocation, DEFAULT_ZOOM.toFloat()))
googleMap?.uiSettings?.isMyLocationButtonEnabled = false
}
}
}
}
} catch (e: SecurityException) {
Log.e("Exception: %s", e.message, e)
}
}
private fun openPlacesDialog() {
val listener = DialogInterface.OnClickListener { dialog, which ->
val markerLatLng = likelyPlaceLatLngs[which]
var markerSnippet = likelyPlaceAddresses[which]
if (likelyPlaceAttributions[which] != null) {
markerSnippet = """
$markerSnippet
${likelyPlaceAttributions[which]}
""".trimIndent()
}
googleMap?.addMarker(MarkerOptions()
.title(likelyPlaceNames[which])
.position(markerLatLng!!)
.snippet(markerSnippet))
googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(markerLatLng,
DEFAULT_ZOOM.toFloat()))
}
AlertDialog.Builder(this.requireContext())
.setTitle(R.string.pick_place)
.setItems(likelyPlaceNames, listener)
.show()
}
companion object {
private const val DEFAULT_ZOOM = 15
private const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1
private const val KEY_CAMERA_POSITION = "camera_position"
private const val KEY_LOCATION = "location"
private const val M_MAX_ENTRIES = 5
}
}
Once done add custom_info_contents.xml file in layout folder:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="locale"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#ff000000"
android:textStyle="bold" />
<TextView
android:id="@+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff7f7f7f" />
</LinearLayout>
Add below code in strings.xml file:
<string name="map_api_key">YOUR_API_KEY</string>
<!--Map strings-->
<string name="title_activity_maps">Current Place Details</string>
<string name="default_info_title">Default Location</string>
<string name="default_info_snippet">No places found, because location permission is disabled.</string>
<string name="option_get_place">Get place</string>
<string name="pick_place">Choose a place</string>
In menu folder create current_place_menu.xml file and add below code:
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/option_get_place"
android:title="@string/option_get_place"
app:showAsAction="always"/>
</menu>
Adding to M D's answer:
From documentation:
A GoogleMap must be acquired using getMapAsync(OnMapReadyCallback). The MapView automatically initializes the maps system and the view.
According to this, the more correct way to initialize GoogleMap
is using getMapAsync
.
Note that your class has to implement OnMapReadyCallback
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_map_page, container, false);
mMapView = (MapView) v.findViewById(R.id.map_view);
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(this); //this is important
return v;
}
@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.getUiSettings().setZoomControlsEnabled(true);
mGoogleMap.addMarker(new MarkerOptions().position(/*some location*/));
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(/*some location*/, 10));
}
@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 onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
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();
}
}