Search code examples
androidgoogle-mapsandroid-fragmentsandroid-viewpager2

MapView/SupportMapFragment graphic glitch while using ViewPager2


I've been trying to use MapView inside a ViewPager fragment and I've encountered a weird bug. MapView keeps glitching like it's trying to re-render and stretching the map weirdly; however, while using zoom/gestures, it's fine. The moment it stops moving it starts glitching again. On Nexus 7 SDK 22 emulator it works fine, while it seems to fail on most of the other emulators/physical devices. It seems it's a hardware rather than a SDK problem. I also tried to just put a SupportMapFragment inside the ViewPager and it behaves the exact same way. Disabling hardware acceleration in the manifest reduces the problem, but it doesn't eliminate it. Country names keep flickering, and the whole screen has a black flicker every few seconds, rather than the stretchy/glitchy bug that happens without the hardware acceleration workaround.

MapViewFragment

public class RealisedVisitsMapFragment extends Fragment implements OnMapReadyCallback {
    private FragmentRealisationVisitsMapBinding binding;

    public static RealisedVisitsMapFragment newInstance(LocalDate date) {
        RealisedVisitsMapFragment fragment = new RealisedVisitsMapFragment();
        return fragment;
    }



    private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = FragmentRealisationVisitsMapBinding.inflate(inflater);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Bundle mapViewBundle = null;
        if (savedInstanceState != null) {
            mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
        }
        binding.mapView.onCreate(mapViewBundle);
        binding.mapView.getMapAsync(this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        Bundle mapViewBundle = outState.getBundle(MAPVIEW_BUNDLE_KEY);
        if (mapViewBundle == null) {
            mapViewBundle = new Bundle();
            outState.putBundle(MAPVIEW_BUNDLE_KEY, mapViewBundle);
        }

        binding.mapView.onSaveInstanceState(mapViewBundle);
    }

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

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

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

    @Override
    public void onMapReady(GoogleMap map) {
        map.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
    }

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

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

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

}

PagerAdapter

public class VisitsFragmentAdapter extends FragmentStateAdapter {
    public VisitsFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);
    }

    public VisitsFragmentAdapter(@NonNull Fragment fragment) {
        super(fragment);
    }


    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position) {
            case 0:
                return SupportMapFragment.newInstance();
            case 1:
                return new RealisedVisitsMapFragment();

            default:
                return null;
        }

    }

    @Override
    public int getItemCount() {
        return 2;
    }
}

Hosting fragment function for setting up the viewpager

   private void setupViewPager() {
        VisitsFragmentAdapter pagerAdapter = new VisitsFragmentAdapter(getChildFragmentManager(), getLifecycle());
        binding.vpVisitsRealization.setUserInputEnabled(false);
        binding.vpVisitsRealization.setAdapter(pagerAdapter);
        binding.vpVisitsRealization.requestTransparentRegion(binding.vpVisitsRealization);
        new TabLayoutMediator(binding.tabLayoutRealization, binding.vpVisitsRealization, (this::setTextByPosition)).attach();
    }

Visual Glitch 1 Visual glitch 2


Solution

  • Resolved by removing viewpager altogether and manually inflating fragments.