Android Maps V2 MapView在自定义片段中(NPE)

22

我不想使用或扩展SupportMapFragmentMapFragment。我有自己的基类,其中包含大量代码。

文档清楚地说明,当某人仅使用MapView时,应调用所有相应的生命周期方法(例如onCreate()onResume()等)。

大多数Fragment中的生命周期方法都类似于Activity,但是当我在我的Fragment之间来回切换时,最终会在onDestroy()onResume()方法中得到模糊的NPE。

所有提供的示例都使用带有MapViewActivity,而不是自定义的Fragment

是否已经有人这样做了?您能否提供在自己的Fragment类中使用MapView的示例代码?

3个回答

35

我对PoPy的答案有些困惑,但最终我成功了,这是我的解决方案。也许对其他遇到此问题的人也有所帮助:

我 struggled a bit with PoPy's answer but in the end I managed it and here is what I came up with. Probably this is helpful to others which will also come across this issue:

public class MyMapFragment extends Fragment {

    private MapView mMapView;
    private GoogleMap mMap;
    private Bundle mBundle;

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

        try {
            MapsInitializer.initialize(getActivity());
        } catch (GooglePlayServicesNotAvailableException e) {
            // TODO handle this situation
        }

        mMapView = (MapView) inflatedView.findViewById(R.id.map);
        mMapView.onCreate(mBundle);
        setUpMapIfNeeded(inflatedView);

        return inflatedView;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBundle = savedInstanceState;
    }

    private void setUpMapIfNeeded(View inflatedView) {
        if (mMap == null) {
            mMap = ((MapView) inflatedView.findViewById(R.id.map)).getMap();
            if (mMap != null) {
                setUpMap();
            }
        }
    }

    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
    }

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

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

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

这里是相应的res/layout/map_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/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

如果您的布局只有一个元素,就像这个例子一样,那么您可以省略RelativeLayout(并将xmlns声明移到您的新根元素上,即com.google.android.gms.maps.MapView)。


1
谢谢你的解决方案,它帮了我很大的忙。不过,你是在使用ViewPager吗?因为我在使用这个解决方案和ViewPager时遇到了问题。我不知道如何保持地图状态(请参见我的评论PoPy的答案以获取更多详细信息)。你能帮我吗? - Sébastien BATEZAT
2
感谢您的实现。我注意到一个问题。当从后堆栈中恢复此地图片段时,地图相机位置会丢失。在我的当前实现中,我使用OnCameraChangeListener将每次更改时的相机位置保存在片段实例变量中。当从后堆栈中恢复片段时,我检查此实例变量,如果不为空,则将地图相机移动到此保存的位置。 - pvshnik
那么如何在我的Activity中添加/使用这个MyMapFragment?你能提供完整的例子吗?我想要动态添加地图视图,并且还想根据屏幕宽度和高度设置地图视图的宽度和高度。请指导我.. - YuDroid
@YuDroid,你应该在这里创建一个新的问题。因为那已经超出了原始帖子的范围。 - Jens Kohl
1
我不得不在Fragment.onDestroyView()中调用MapView.onDestroy()并清除MapView引用,否则恢复的片段将显示一个空的灰色地图(这有点合理,因为在Fragment.onCreateView()中调用MapView.onCreate())。 - mmathieum
显示剩余3条评论

13
我已成功将一个MapView(v2)包含在自定义Fragment中,并将其嵌入ViewPager中。在我的情况下,MapView包含在Fragment布局文件中。我必须对MapView调用生命周期方法(从Fragment的onCreate()调用onCreateView()),并手动调用MapsInitializer.initialize(context)以避免BitmapDescriptorFactory类中出现NullPointerException异常(以获取标记的位图)。最后这个技巧很奇怪,我不知道为什么地图系统没有正常初始化而需要调用它,可能只是当前版本中的一个错误...。
在我的情况下,我在onResume()或onDestroy()中没有遇到任何NullPointerException。

以下是来自官方文档关于MapsInitializer.initialize(context)的一些细节: BitmapDescriptorFactory MapsInitializer - PoPy
1
我已经有一个Map对象,但出现异常的原因仍然存在。当我添加了这个静态丑陋的调用时,它最终起作用了。 - dnkoutso
@PoPy 我正在尝试做和你一样的事情(即:在viewPager中包含mapView),但是你是如何保持mapView状态(标记,位置,缩放等)的呢?当我从第1页(带有地图)跳转到第3页,然后返回第1页时,我的状态就丢失了! - Sébastien BATEZAT
@Sebastien 我目前还没有尝试保存状态,所以很抱歉我无法在这方面为您提供帮助。 - PoPy
@Sebastien 我相信MapView.onSaveInstanceState方法可以解决你遇到的问题。 - Pijusn

-2

在使用单独的MapView时,有两个非常重要的事情

    //at Activity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      MapsInitializer.initialize(this);
      mapView.onCreate(savedInstanceState); 
    }
    //or at Fragment
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                       Bundle savedInstanceState) {

       MapsInitializer.initialize(getActivity());  
       mapView.onCreate(mBundle);
     }
    //along with the following
    @Override
    protected void onResume() {
       super.onResume();
       if (mapView != null)
        mapView.onResume();
      }

    @Override
    protected void onDestroy() {
       super.onDestroy();
       if (mapView != null)
        mapView.onDestroy();
    }

    @Override
    public void onLowMemory() {
      super.onLowMemory();
      if (mapView != null)
        mapView.onLowMemory();
    }

@martyonair:我在哪里调用了两次?我说过在使用它的Activity或Fragment中。请仔细阅读后再进行投票。 - laaptu
@laaptu 你在哪里初始化了mapView - Marcel Bro
@anoniim,您可以在“Activity”或“Fragment”上初始化它,无论您在哪里使用它。 - laaptu
@laaptu 我的意思是哪个方法?在你的代码片段中看不到它。 - Marcel Bro
@anoniim 在 setContentView 后的 ActivityonCreate() 或者 FragmentonCreateView 中,你可以添加类似于 findViewById(R.id.yourgivenid) 的代码。 - laaptu

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