如何在旋转设备(竖屏/横屏)后保留/重新绑定MapFragment上的事件监听器?

10

我正在使用Xamarin(版本7.1)开发一款Android应用程序。它显示为地图,并在OnCameraIdle()中绘制PolyLines。

MapFragmentOnCreate中以编程方式生成。我通过GetMapAsyncOnResume中获取GoogleMap并在OnMapReady中绑定侦听器。
它们很好用,但只有在一开始的时候。一旦设备旋转(纵向->横向或反之亦然),摄像机移动就不再触发侦听器了。
地图仍然可以工作-用户仍然可以很好地移动摄像机。我(应用程序)只是不能再使用它了。

以下是裸代码,只包括地图创建和处理。所有其他内容(实际绘图)都被删除:

public class MapActivity : Activity, IOnMapReadyCallback, 
    GoogleMap.IOnCameraIdleListener, GoogleMap.IOnCameraMoveStartedListener
{
    private GoogleMap _map;
    private MapFragment _mapFragment;

    private void InitializeMap()
    {
        _mapFragment = MapFragment.NewInstance();
        var tx = FragmentManager.BeginTransaction();
        tx.Add(Resource.Id.map_placeholder, _mapFragment);
        tx.Commit();
    }

    private void SetMapListeners()
    {
        Log.Debug("MyApp/ Map", "SetMapListeners");
        _map.SetOnCameraIdleListener(this);
        _map.SetOnCameraMoveStartedListener(this);
    }

    /* Activity */

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Log.Debug("MyApp / Map", "OnCreate");
        SetContentView(Resource.Layout.Map);
        InitializeMap();
    }

    protected override void OnStart()
    {
        base.OnStart();
        Log.Debug("MyApp / Map", "OnStart");
    }

    protected override void OnResume()
    {
        base.OnResume();
        if (_map == null)
            _mapFragment.GetMapAsync(this);
        Log.Debug("MyApp / Map", "OnResume");
    }

    protected override void OnPause()
    {
        base.OnPause();
        Log.Debug("MyApp / Map", "OnPause");
    }

    protected override void OnStop()
    {
        base.OnStop();
        Log.Debug("MyApp / Map", "OnStop");
    }

    protected override void OnDestroy()
    {
        base.OnStop();
        Log.Debug("MyApp/ Map", "OnDestroy");
    }


    /* IOnMapReadyCallback */   

    public void OnMapReady(GoogleMap googleMap)
    {
        Log.Debug("MyApp / Map", "Map is ready!");
        _map = googleMap;       
        SetMapListeners();
    }


    /* IOnCameraIdleListener */

    public void OnCameraIdle()
    {
        Log.Debug("MyApp / Map", "Camera is idle.");
        // Drawing routine is called here
    }


    /* IOnCameraMoveStartedListener */

    public void OnCameraMoveStarted(int reason)
    {
        Log.Debug("MyApp / Map", "Camera move started.");
    }
}

通过以下日志摘录可以看到,监听器在开始时确实起作用,但一旦设备旋转(至少)一次,它们就消失了。
我还尝试仅在生命周期中的第一次调用OnMapReady时调用SetMapListeners,但这并没有改变任何内容。

04-03 20:29:06.486 D/MyApp / Map( 7446): OnCreate
04-03 20:29:06.688 I/Google Maps Android API( 7446): Google Play services client version: 10084000
04-03 20:29:06.695 I/Google Maps Android API( 7446): Google Play services package version: 10298438
04-03 20:29:07.394 D/MyApp / Map( 7446): OnStart
04-03 20:29:07.399 D/MyApp / Map( 7446): OnResume
04-03 20:29:07.432 D/MyApp / Map( 7446): Map is ready!
04-03 20:29:07.438 D/MyApp / Map( 7446): SetMapListeners
04-03 20:29:07.568 D/MyApp / Map( 7446): Camera is idle.
04-03 20:29:09.231 D/MyApp / Map( 7446): Camera move started.
04-03 20:29:09.590 D/MyApp / Map( 7446): Camera is idle.
04-03 20:29:12.350 D/MyApp / Map( 7446): Camera move started.
04-03 20:29:12.751 D/MyApp / Map( 7446): Camera is idle.

## Listeners are responding, now rotating the device.

04-03 20:29:15.503 D/MyApp / Map( 7446): OnPause
04-03 20:29:15.508 D/MyApp / Map( 7446): OnStop
04-03 20:29:15.572 D/MyApp / Map( 7446): OnDestroy
04-03 20:29:15.595 I/Google Maps Android API( 7446): Google Play services package version: 10298438
04-03 20:29:15.596 D/MyApp / Map( 7446): OnCreate
04-03 20:29:15.628 I/Google Maps Android API( 7446): Google Play services package version: 10298438
04-03 20:29:15.655 D/MyApp / Map( 7446): OnStart
04-03 20:29:15.655 D/MyApp / Map( 7446): OnResume
04-03 20:29:15.690 D/MyApp / Map( 7446): Map is ready!
04-03 20:29:15.691 D/MyApp / Map( 7446): SetMapListeners

## Map is rotated, camera position was preserved. 
## Now moving the camera, but no listeners are responding.

04-03 20:29:24.436 D/MyApp / Map( 7446): OnPause
04-03 20:29:31.288 D/MyApp / Map( 7446): OnStop
04-03 20:29:31.359 D/MyApp / Map( 7446): OnDestroy

对我来说,有趣的是当我切换回先前的活动并再次打开地图时,它会重新开始工作。但是,如您在日志中所见,在旋转设备时,该活动也被销毁并重新创建。据我所知,碎片不是这样,所以也许那是个提示。我不知道。

我还尝试在OnDestroy中删除侦听器(通过设置null),但这也没有改变任何东西。

您有什么想法,我可能做错了什么吗?


1
我的建议是,在设备旋转时,活动和片段都会被销毁,然后重新创建。更多信息请参考:https://developer.android.com/guide/components/fragments.html#Lifecycle - user1506104
1
谢谢。我曾经有一个没有证实的印象,即Fragment不会被重新创建,可能是因为视口被保留了,但这当然可以传递。 - Imanuel
尝试检查savedInstanceState以查看您的Activity是否是第一次构建还是仅返回它。您似乎在每次调用时重叠了片段。 - gustavogbc
1
感谢gustavogbc的帮助,问题已解决!“您似乎重叠了片段”-我在我的活动中添加了ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,现在它可以正常工作了。尽管检查savedInstanceState(仅在其为null时创建MapFragment)没有帮助。请将此发布为答案,以便我可以授予您赏金。 - Imanuel
谢谢你,法老大人。我刚刚发布了一个答案。希望你的应用程序能够做得很好! - gustavogbc
4个回答

5
尝试将InitializeMap()调用放在OnCreateView()方法的覆盖中,而不是OnCreate()中。

1
在我看来,OnCreateViewFragment 类的一部分。你是说我应该扩展 MapFragment 吗? - Imanuel
不,应该在MapActivity中创建OnCreateView的覆盖(应该可用)。并且将initializemap的调用放在OnCreate中当前调用的位置。 - App Pack
好的,OnCreateViewActivity 的一部分,我没有注意到。但这并没有帮助——相机位置和缩放是保持不变的(与之前的 OnCreate 相同),但在旋转设备后没有触发监听器。 - Imanuel

4

默认情况下,当屏幕旋转时,您的Activity会被销毁并重新启动。为了确保不丢失数据,您需要使用生命周期方法正确保存和恢复数据。请参阅SavingPersistentStatesavedInstanceState,并遵循此Android开发人员文档。希望这可以帮助到您!


没有数据会丢失 - 唯一需要保留的是相机位置,但这是自动保存的。只有重新连接监听器与上面的代码不起作用。 - Imanuel

4

我遇到了类似的问题,因为我们的MapFragment嵌套在另一个Fragment中,所以我们每次都需要重新添加MapFragment。由于您直接将其嵌入Activity中,@App Pack也应该有效,但我仍然会粘贴代码。

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();  //onCreateView() or onResume() should work too

    FragmentManager fragmentManager = ((AppCompatActivity) getContext()).getSupportFragmentManager();
    SupportMapFragment mapFragment = SupportMapFragment.newInstance();
    fragmentManager.beginTransaction()
        .replace(R.id.mapContainer, mapFragment)
        .commit();
    mapFragment.getMapAsync(this);
}

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

    UiSettings uiSettings = googleMap.getUiSettings();
    uiSettings.setZoomControlsEnabled(false);

    //do your initialization + recreate your last map position from BundleSavedIntance: check this answer: https://dev59.com/7XLYa4cB1Zd3GeqPXmwY#16698624
}

请注意:函数名称应该以小写字母开头!!!


1
在C#/Xamarin中,函数以大写字母开头。你的代码基本上在OnAttachedToWindow中调用了InitializeMap() - 我现在尝试了一下,但行为是相同的。你在结尾处的评论让我有点惊讶,因为当我旋转设备时,地图位置是保持不变的。我认为MapFragment会自动处理这个问题。 - Imanuel
哦,抱歉,我没有看到 Xamarin 标签。请忽略我的评论。 - longi

2

尝试检查savedInstanceState,以查看您的Activity是第一次构建还是仅返回它。您似乎在每次调用时重叠了片段。


谢谢 - 虽然不是100%的答案,但这指引了我正确的方向。检查savedInstanceState并且只有在它为null时才创建MapFragment没有帮助,但为了避免重叠片段,我在我的活动中添加了ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize。现在它可以工作了。 - Imanuel

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