1

I have 3 tabs:

  • Home
  • Google Map
  • Settings

I have a main activity which has a TabLayout to show the 3 tabs:

public class MainActivity extends AppCompatActivity implements MyMapFragment.OnFragmentInteractionListener, SettingsFragment.OnFragmentInteractionListener {

private static final int INDEX_HOME_FRAGMENT = 0;
private static final int INDEX_MAP_FRAGMENT = 1;
private static final int INDEX_SETTINGS_FRAGMENT = 2;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);

    tabLayout.addTab(tabLayout.newTab().setText("Home"));
    tabLayout.addTab(tabLayout.newTab().setText("Map"));
    tabLayout.addTab(tabLayout.newTab().setText("Settings"));

    tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {

            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();


            switch(tab.getPosition()) {

                case INDEX_HOME_FRAGMENT:
                    HomeFragment homeFragment = new HomeFragment();
                    transaction.replace(R.id.frame_layout, homeFragment);
                    // Commit the transaction
                    transaction.commit();

                    break;

                case INDEX_MAP_FRAGMENT:
                    MyMapFragment myMapFragment = new MyMapFragment();

                    transaction.replace(R.id.frame_layout, myMapFragment);
                    // Commit the transaction
                    transaction.commit();

                    break;

                case INDEX_SETTINGS_FRAGMENT:
                    SettingsFragment settingsFragment = new SettingsFragment();
                    transaction.replace(R.id.frame_layout, settingsFragment);
                    // Commit the transaction
                    transaction.commit();
                    break;

                default:
                    //error occured here
                    Log.e("Error occured = ", " TAB_ERROR");
                    break;
            }
        }
    });

    // Check that the activity is using the layout version with
    // the fragment_container FrameLayout
    if (findViewById(R.id.frame_layout) != null) {
        if (savedInstanceState != null) {
            return;
        }
        HomeFragment homeFragment = new HomeFragment();
        // Add the fragment to the 'fragment_container' FrameLayout
        getSupportFragmentManager().beginTransaction()
                .add(R.id.frame_layout, homeFragment ).commit();
    }
}

@Override
public void onFragmentInteraction(Uri uri) {
    //do nothing
}
}

This Activity also handles when a tab gets clicked. When a tab gets clicked it should show the Fragment in the FrameLayout.
This all works fine. The only problem is when I click the Maps tab, Google maps always refreshes.
I want the map to preload when the app starts so when the user clicks the map tab the map shoudl already be loaded. As well when the user goes away from the map tab and comes back it should save the map as it was previously. Currently when the user goes back to the map tab the map is reloaded and it takes again some time for it to load.

This is my map Fragment:

public class MyMapFragment extends SupportMapFragment
        implements OnMapReadyCallback,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener {

    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
    GoogleMap mGoogleMap;
    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;
    Marker lastOpenned = null;


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

        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mGoogleMap == null) {
            getMapAsync(this);
        }
    }

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

        //stop location updates when Activity is no longer active
        if (mGoogleApiClient != null) {
            //LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,this);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        mGoogleMap = googleMap;


        LatLngBounds.Builder builder = new LatLngBounds.Builder();

        mGoogleMap.addMarker(new MarkerOptions()
                .position(new LatLng(40.76793169992044, -73.98180484771729))
                .title("San Francisco"));
        mGoogleMap.addMarker(new MarkerOptions()
                .position(new LatLng(41.76793169992044, -72.98180484771729))
                .title("Las Vegas"));

        builder.include(new LatLng(40.76793169992044, -73.98180484771729));
        builder.include(new LatLng(41.76793169992044, -72.98180484771729));
        LatLngBounds bounds = builder.build();

        mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 200));

        mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
            public boolean onMarkerClick(Marker marker) {

                return true;
            }
        });

        //Initialize Google Play Services
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(getActivity(),
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                //Location Permission already granted
                buildGoogleApiClient();
                mGoogleMap.setMyLocationEnabled(true);
            } else {
                //Request Location Permission
                checkLocationPermission();
            }
        } else {
            buildGoogleApiClient();
            mGoogleMap.setMyLocationEnabled(true);
        }
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnected(Bundle bundle) {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        if (ContextCompat.checkSelfPermission(getActivity(),
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
    }


    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onLocationChanged(Location location) {

    }

    private void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
                    Manifest.permission.ACCESS_FINE_LOCATION)) {

                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(getActivity())
                        .setTitle("Location Permission Needed")
                        .setMessage("This app needs the Location permission, please accept to use location functionality")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(getActivity(),
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            }
                        })
                        .create()
                        .show();


            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(getActivity(),
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION);
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(getActivity(),
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) {

                        if (mGoogleApiClient == null) {
                            buildGoogleApiClient();
                        }
                        mGoogleMap.setMyLocationEnabled(true);
                    }

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(getActivity(), "permission denied", Toast.LENGTH_LONG).show();
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request
        }
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }


    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
}
Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
user2924127
  • 6,034
  • 16
  • 78
  • 136

1 Answers1

0

I would suggest not making new MyMapFragment() every time you click the tab if you don't want the map to reload.

A simple way to do that is a simple wrapper Fragment holder class.

public class MainActivity extends AppCompatActivity 
    implements MyMapFragment.OnFragmentInteractionListener, SettingsFragment.OnFragmentInteractionListener {

    static class FragmentTab {
        String name;
        Fragment frag;

        public FragmentTab(String name, Fragment f) {
            this.name = name;
            this.frag = f;
        }
    }

    // These are only created once, not per-click
    private List<FragmentTab> tabs = Arrays.asList(
        new FragmentTab("Home", new HomeFragment()),
        new FragmentTab("Map", new MyMapFragment()),
        new FragmentTab("Settings", new SettingsFragment()),
    );

Then, you can iterate and index that list to clean up the rest of your code

for (FragmentTab tab : tabs) {
    tabLayout.addTab(tabLayout.newTab().setText(tab.name));
}

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {

        getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.frame_layout, tabs.get(tab.getPosition()).frag)
            .commit();
    }
};

A LinkedHashMap<String, Fragment> would also work, but replacing tab.getPosition() for tab.getTitle() (I think)

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • Thanks for the reply. I have implemented the code, but unfortunately the map fragment is still not preloaded. When I turn on the app and wait for some time then click the map tab, the map then just begins to get loaded. When I click the home tab again and then click the map tab again, the map still refreshes and it doesn't save the map as it was (the zoom level, missing markers, etc). – user2924127 Apr 08 '17 at 22:57
  • You probably need to save those within the Fragment's `onSaveInstanceState` and restore the necessary values in `onCreateView`, then – OneCricketeer Apr 08 '17 at 23:02
  • Sorry I am maybe missing something. If I had to put the zoom level, markers back it would then need animate and zoom in and all of that. I would like it to have the map exactly as it was previously instantly without the map zooming back and readding the markers. Also would this then preload the map fragment when the app starts? Essentially previously I was using viewpager. With viewpager using the setOffscreenPageLimit() I was able to preload the pages(fragments) and when I swiped/click tab the pages were just as they were previously. I am trying to mimic the same behavior. – user2924127 Apr 08 '17 at 23:15
  • If the ViewPager worked before, why did you remove it? See "ViewPager integration". https://developer.android.com/reference/android/support/design/widget/TabLayout.html – OneCricketeer Apr 08 '17 at 23:16
  • I would like to use viewpager, but ran into an issue I couldn't solve: http://stackoverflow.com/questions/43290476/viewpager-in-the-fragment-pages-create-a-new-fragment so I am now trying a different solution. – user2924127 Apr 08 '17 at 23:19
  • That comment there is exactly what I did here "store it in list and return it with appropriate position" – OneCricketeer Apr 08 '17 at 23:21
  • Sorry, would you mind showing the code to do this? I think I just not understanding. In my fragment I have overrode the onSaveInstance and onCreateView, but I am probably doing something incorrect. – user2924127 Apr 08 '17 at 23:25
  • You'd be better off using the ViewPager if that was working as expected – OneCricketeer Apr 08 '17 at 23:29