MapView doesn't show google map in Android mvvm databinding [SOLVED]

Viewed 1150

I'm trying to setup google maps using mapView with data binding and set some markers on map. My map works ok when i hardcode "latlngs", but when i try to set "latlngs" which i get from the server, map doesn't show up. Markers are shown, there is no problem with data and setting markers, only map doesn't work. And google logo is there, in case you wonder if my API key is valid. Here is my code.

This is Fragment

private MapView google_map;

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

}

@Override
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    FragmentMapBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_map, container, false);
    MapFragmentViewModel viewModel = new ViewModelProvider(this, new MapFragmentViewModelFactory()).get(MapFragmentViewModel.class);

    binding.setLifecycleOwner(this);
    binding.setViewModel(viewModel);
    
    google_map = binding.googleMap;

    viewModel.getLocations();
    viewModel.locationsResponse().observe(getViewLifecycleOwner(), apiResponseObject -> {
        switch (apiResponseObject.status) {
            case SUCCESS:
                ViewUtils.hideProgressDialog();
                break;
            case LOADING:
                ViewUtils.showProgressDialog(requireContext());
                break;
            case ERROR:
                ViewUtils.hideProgressDialog();
                ViewUtils.showToastMessage(requireContext(), apiResponseObject.error);
        }
    });

    setTypeFace(binding.getRoot(), requireContext());
    languageSetup(requireContext());
    return binding.getRoot();
}

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

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

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

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

@Override
public void onStop() {
    super.onStop();
    google_map.onStop();
}

This is my viewModel:

private MutableLiveData<ApiResponseObject> responseLiveData = new MutableLiveData<>();
private CompositeDisposable disposables;
private ApiCallsRepository apiCallsRepository;

public MapFragmentViewModel() {
    apiCallsRepository = new ApiCallsRepository();
    disposables = new CompositeDisposable();

}

void getLocations() {

    disposables.add(apiCallsRepository.getLocations(SharedPrefUtils.getUserId())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSubscribe(disposable -> responseLiveData.setValue(ApiResponseObject.loading()))
            .subscribe(this::setLocations, this::handleError));

}

LiveData<ApiResponseObject> locationsResponse() {
    return responseLiveData;
}

public MutableLiveData<List<LatLng>> locations = new MutableLiveData<>();

@BindingAdapter("initMap")
public static void initMap(final MapView mapView, final List<LatLng> locations) {

    if (mapView != null) {
        mapView.onCreate(new Bundle());
        mapView.getMapAsync(googleMap -> {
            if (locations != null) {
                for (LatLng latLng : locations)
                    googleMap.addMarker(new MarkerOptions().position(latLng));
            }
        });
    }
}

public void setLocations(List<LocationsResponse> mLocationsResponses) {

    List<LatLng> latLngs = new ArrayList<>();

    for (LocationsResponse locationsResponse : mLocationsResponses)
            latLngs.add(new LatLng(Double.parseDouble(locationsResponse.getLongitude()), 
                    Double.parseDouble(locationsResponse.getLatitude())));

    locations.setValue(latLngs);
    responseLiveData.setValue(ApiResponseObject.success(null));
}

private void handleError(Throwable throwable) {
    if (throwable instanceof HttpException) {
        HttpException httpException = (HttpException) throwable;
        Response<?> response = httpException.response();
        responseLiveData.setValue(ApiResponseObject.error(getErrorMessage(response)));
    } else if (throwable instanceof IOException) {
        responseLiveData.setValue(ApiResponseObject.error(UNEXPECTED_ERROR));
    } else {
        responseLiveData.setValue(ApiResponseObject.error(throwable.getMessage()));
    }
}

map in xml is like this

<com.google.android.gms.maps.MapView
        android:id="@+id/google_map"
        android:name="com.google.android.gms.maps.MapFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:initMap="@{viewModel.locations}"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvTextWarning" />

and server response

[{
"Latitude": "19.8164793",
"Longitude": "45.2628729",
"Elevation": "128.6999969482422",
"RecordedAt": "2020-09-30T09:32:56"},{
"Latitude": "19.8164799",
"Longitude": "45.2628755",
"Elevation": "129.60000610351562",
"RecordedAt": "2020-09-30T10:35:42"},{
"Latitude": "19.8164801",
"Longitude": "45.262874",
"Elevation": "129.60000610351562",
"RecordedAt": "2020-09-30T10:34:04"}]

I'VE FOUND THE SOLUTION

The problem was "locations" mutableLiveData being set again on response call, and that calls getMapAsync again and i needed to add mapView.onResume inside that method.

@BindingAdapter("initMap")
public static void initMap(final MapView mapView, final List<LatLng> locations) {

    if (mapView != null) {
        mapView.onCreate(new Bundle());
        mapView.getMapAsync(googleMap -> {
            if (locations != null) {
                mapView.onResume();
                for (LatLng latLng : locations)
                    googleMap.addMarker(new MarkerOptions().position(latLng));
            }
        });
    }
}
1 Answers

You need to load map in Fragment, not in ViewModel. Move this code to fragment's onViewCreated

mapView.getMapAsync(googleMap -> {
        if (locations != null) {
            for (LatLng latLng : locations)
                googleMap.addMarker(new MarkerOptions().position(latLng));
        }
    });
Related