Android中Fragment中的MapView

28
我希望在我的Fragment中拥有MapView。这是我的FragmentLayout 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"
    android:background="#17df0d"
    android:orientation="vertical" >

<com.google.android.gms.maps.MapView
    android:id="@+id/mapview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@+id/textView1"
    android:layout_alignParentLeft="true"
    android:layout_alignParentRight="true"
    android:layout_marginBottom="70dp" >

</com.google.android.gms.maps.MapView>
</RelativeLayout>

我的AndroidManifest.xml文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="a.b.c.d"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="your_api_key" />
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />


    </application>

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <permission
        android:name="a.b.c.d.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />

    <uses-permission android:name="a.b.c.d.permission.MAPS_RECEIVE" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

</manifest>

和我的Fragment类

public class ReportFragment extends Fragment implements LocationListener {
    MapView mapView = null; //eventually it is being read from view and assigned
当我启动应用程序时,在我的片段中没有看到任何地图视图。

1
你的MapView在布局中的哪个位置? - M D
你没有添加 mapview 到任何地方,所以你看不到它。 - Rustam
2
我认为你正在使用GoogleMap v2,所以最好使用MapFragmentSupportMapFragment代替MapView - Piyush
@user3833308 好的。请查看我的答案并实现您的MapView。 - M D
@user3833308 请参考此链接:http://www.codota.com/android/scenarios/52c5d573da0a160bd378852b/com.google.android.gms.maps.MapFragment?tag=dragonfly - Piyush
显示剩余2条评论
5个回答

46

来自Josh Holtz在GitHub上的示例

您应该在Layout中添加MapView,例如:

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

并实现您的Fragment,如下所示

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

}

5
我在使用MapView时遇到了一些内存泄漏问题,你有类似的经历吗? - Daniel Gomez Rico
是的,我也有这个问题。你找到任何解决方案了吗? - Harshvardhan Trivedi
同样的问题,但没有解决方案。 - andrei
这里也有内存泄漏,有什么建议吗? - Tunji_D
1
使用MapFragment代替MapView - M D
2
尝试在onDestroyView中调用mapView.onDestroy()。 - cVoronin

22

补充M D的回答:

根据文档

必须使用getMapAsync(OnMapReadyCallback)获取GoogleMap。 MapView会自动初始化地图系统和视图。

因此,更正确的初始化GoogleMap的方法是使用getMapAsync

请注意,您的类必须实现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();
}

9

如果有人在寻找 Kotlin 版本的 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"))
}

谢谢,这对我有用,除了我必须将“mMap”声明为“MapView?”而不是“GoogleMap?” - nicolas leo

2

虽然是旧帖子,但对于正在寻求在片段内实现地图的人可能有所帮助。以下是使用Kotlin的详细示例:

首先,在AndroidManifest.xml文件中添加权限:

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

然后,在AndroidManifest.xml文件的应用程序标记下添加元数据:
<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" />

*获取API链接,请访问:https://developers.google.com/maps/documentation/android-sdk/get-api-key

在build.gradle文件中实现play服务:

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'

添加插件: apply plugin: 'com.google.gms.google-services'

在您的fragment.xml文件中添加地图片段:

<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"/>

在你的fragment.kt文件中,实现OnMapReadyCallback接口。使用getMapAsync()初始化谷歌地图。以下代码可让你集成谷歌地图并指向你的当前位置。
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
    }
}

完成后,请将custom_info_contents.xml文件添加到layout文件夹中:
<?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>

在strings.xml文件中添加以下代码:

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

在菜单文件夹中创建current_place_menu.xml文件,并添加以下代码:
<?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>

希望这能帮到你...
(注:保留html标签)

0
确保您重写或调用了OnDestroyView,以及OnDestroy。类似于...
    public override void OnDestroy()
    {            
        base.OnDestroy();
        MapView.OnDestroy();
        OnDestroyView();            
    }

    public override void OnDestroyView()
    {            
        base.OnDestroyView();
        mapReadyCallbackList.Clear();            
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接