片段中的android MapView

Posted

技术标签:

【中文标题】片段中的android MapView【英文标题】:android MapView in Fragment 【发布时间】:2014-11-28 06:01:36 【问题描述】:

我想在我的Fragment 中加入MapView

这是我的FragmentLayout xml 文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:background="#17df0d"
    android:orientation="vertical" >

<com.google.android.gms.maps.MapView
    android:id="@+id/mapview"
    android:layout_
    android:layout_
    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

当我启动应用程序时,我在 Fragment 中看不到任何地图视图

【问题讨论】:

你的MapView在你的布局中在哪里? 您确实看到了它,因为您没有在任何地方添加mapview 我认为您使用的是GoogleMap v2,所以最好使用MapFragmentSupportMapFragment 而不是MapView 我可以在 Fragment 中使用 Fragment 吗? @X'因素 @user3833308 好的。查看我的答案并实施您的 MapView。 【参考方案1】:

来自Josh Holtz's example on GitHub:

你应该在你的Layout 中添加MapView 喜欢

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

并实现你的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();
  


【讨论】:

我在使用 MapView 时遇到了一些内存泄漏,有类似的经历吗? 是的,我也有。你找到解决办法了吗? 这里一样,但没有解决办法 MapFragment代替MapView 尝试从onDestroyView调用mapView.onDestroy()【参考方案2】:

添加到 M D 的答案:

来自documentation:

必须使用 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();

【讨论】:

【参考方案3】:

如果有人正在寻找 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?【参考方案4】:

确保您覆盖或调用 OnDestroyView 以及 OnDestroy。有点像...

    public override void OnDestroy()
                
        base.OnDestroy();
        MapView.OnDestroy();
        OnDestroyView();            
    

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

【讨论】:

【参考方案5】:

旧帖子,但可能对希望在片段中实现地图的人有所帮助。下面是使用 kotlin 的详细示例:

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

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

然后在AndroidManifest.xml文件的application标签下添加元数据:

<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 文件中实现播放服务:

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'

添加插件:应用插件:'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_
          android:layout_/>

在您的 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 文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_
              android:layout_
              android:layoutDirection="locale"
              android:orientation="vertical">
    <TextView
            android:id="@+id/title"
            android:layout_
            android:layout_
            android:layout_gravity="center_horizontal"
            android:textColor="#ff000000"
            android:textStyle="bold" />

    <TextView
            android:id="@+id/snippet"
            android:layout_
            android:layout_
            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>

希望这会有所帮助...

【讨论】:

以上是关于片段中的android MapView的主要内容,如果未能解决你的问题,请参考以下文章

Android中的片段事务问题

Android片段中的动态背景

如何用 ViewPager 中的另一个片段替换 Android 片段?

Android - 片段中的 getIntent()

如何在 Android 中的特定片段容器中显示片段

android片段-当另一个片段被推到它上面时如何保存片段中的视图状态