Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retain a map view inside a fragment?

I have a use case where a list and a map view share quite the same code base and show the same data. So I cannot separate them by using ListFragment and MapFragment as parent.

So I made a fragment that contains both views:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible"/>

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

</RelativeLayout>

The user can toggle between list and map view. This works very well despite the fact that the map view cannot retain its markers on configuration changes. When the screen is rotated, the map view get's its initial state without markers and without zoom. All markers that where previously added are gone. When I used MapFragment I did not have this problem, it kind of retained itself.

Stripped down code:

public class ListAndMapFragment extends Fragment {

    private MapView mapView;

    public ListAndMapFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

     @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle arguments) {
        super.onCreateView(inflater, container, arguments);
        View root = inflater.inflate(R.layout.fragment_pillar_list, container, false);
        mapView = (MapView) root.findViewById(R.id.mapView);

        if (arguments == null) {
            mapView.onCreate(null);
            MapsInitializer.initialize(this.getActivity());
        }

        return root;
    }

    private GoogleMap getMap() {
        return mapView.getMap();
    }

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

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

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }
}

Any ideas how to address this problem? One solution would be to replace all markers. However I would like to know if there is another approach.

like image 807
UpCat Avatar asked Oct 13 '14 05:10

UpCat


People also ask

Can I display a mapview inside a fragment?

Now there is all sorts of documentation that show how to use a MapFragment to display a MapView. Unfortunately if you are looking to just include a MapView inside of a Fragment, that documentation does you no good.

What is a fragment in Android?

In Android, the fragment is the part of Activity that represents a portion of the User Interface (UI) on the screen. It is the modular section of the android activity that is very helpful in creating UI designs that are flexible in nature and auto-adjustable based on the device screen size.

How to initialize googlemap in mapview?

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.

How do I nest a fragment in an activity?

To nest a fragment, simply call getChildFragmentManager () on the Fragment in which you want to add a fragment. This returns a FragmentManager that you can use like you normally do from the top-level activity to create fragment transactions. For example, here’s some code that adds a fragment from within an existing Fragment class:


1 Answers

Marker object cannot be retained through configuration changes because it doesn't implement Parcelable or Serializable interface.

However, you can persist MarkerOptions object because it is Parcelable and then recreate your Markers from it. You can have a class field of type MarkerOptions for each of your Markers and then re-populate the map on configuration changes (for example in onSaveInstanceState):

mapView.getMap().addMarker(MyMarkerOptions);

Another way would be to save your Markers in a HashMap and re-use them on configuration changes.

Here is another full example that uses Parcelable LatLng points to persist Markers.

The example works so when adding a marker it also adds the corresponding coordinates to an ArrayList as LatLng object. Then on configuration change, the ArrayList gets saved to a Bundle object in onSaveInstanceState and the LatLng objects get retrieved in onCreate.


OP's note:

So I went with the solution to implement onSaveInstanceState and drop setRetainInstance(true). The benefit is that I can retain the map myself. However the disadvantage is that the map has to be fully initialized on each config change which makes the app slow for 1 or 2 seconds on e.g. screen rotation. But I am fine with this.

like image 68
nem035 Avatar answered Oct 21 '22 08:10

nem035