MapView in a Fragment (Honeycomb)

now that the final SDK is out with google apis – what is the best way to create a Fragment with a MapView? MapView needs a MapActivity to work right.

Having the Activity managing the Fragments inherit from MapActivity (bad solution because it goes against the idea that Fragments are self contained) and use a regular xml based layout does not work. I get a NullPointerException in MapActivity.setupMapView():

  • JSON file VS SQLite android
  • splash screen application and hide action bar
  • Browsing multiple photos in flash for android
  • How to divide irregular polygon into equal areas on Google map V2
  • PreferenceScreen android:summary update !
  • Android Studio “Current NDK support is deprecated”
  • E/AndroidRuntime(  597): Caused by: java.lang.NullPointerException
    E/AndroidRuntime(  597):    at com.google.android.maps.MapActivity.setupMapView(MapActivity.java:400)
    E/AndroidRuntime(  597):    at com.google.android.maps.MapView.(MapView.java:289)
    E/AndroidRuntime(  597):    at com.google.android.maps.MapView.(MapView.java:264)
    E/AndroidRuntime(  597):    at com.google.android.maps.MapView.(MapView.java:247)
    

    My second idea was to create the MapView programmatically and pass the associated activity (via getActivity()) as Context to the MapView constructor. Does not work:

    E/AndroidRuntime(  834): Caused by: java.lang.IllegalArgumentException: MapViews can only be created inside instances of MapActivity.
    E/AndroidRuntime(  834):    at com.google.android.maps.MapView.(MapView.java:291)
    E/AndroidRuntime(  834):    at com.google.android.maps.MapView.(MapView.java:235)
    E/AndroidRuntime(  834):    at de.foo.FinderMapFragment.onCreateView(FinderMapFragment.java:225)
    E/AndroidRuntime(  834):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:708)
    E/AndroidRuntime(  834):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:900)
    E/AndroidRuntime(  834):    at android.app.FragmentManagerImpl.addFragment(FragmentManager.java:978)
    E/AndroidRuntime(  834):    at android.app.Activity.onCreateView(Activity.java:4090)
    E/AndroidRuntime(  834):    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:664)
    

    Really there should be something like MapFragment that takes care of the background threads MapView needs I guess… So what is the current best practice to do this?

    Thanks and regards from Germany,
    Valentin

  • Android: how to wait AsyncTask to finish in MainThread?
  • Referencing the user's home directory in a Gradle script
  • Is it possible to remove AM/PM button from TimePicker?
  • The apk must be signed with the same certificates as the previous version
  • How to get application or package info from the apk file in the android application in the Android environment
  • How to remove email icon from Android Studio emulation
  • 12 Solutions collect form web for “MapView in a Fragment (Honeycomb)”

    As of 03.12.2012 Google released Google Maps Android API v2. Now you can forget about these problems.
    https://developers.google.com/maps/documentation/android/

    Example using new API – https://developers.google.com/maps/documentation/android/start#add_a_map

    This API will work for at least Android API 8, so use it ;).

    So now you can simply use “com.google.android.gms.maps.MapFragment” fragment class. It will display the map in your Activity. Layout example from the link above:

    <?xml version="1.0" encoding="utf-8"?>
    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment"/>
    

    I’ve managed to resolve this by using TabHost in fragment.

    Here is the idea (briefly):

    1. MainFragmentActivity extends FragmentActivity (from support library) and has MapFragment.

    2. MyMapActivity extends MapActivity and contain MapView.

    3. LocalActivityManagerFragment hosts LocalActivityManager

    4. MapFragment extends LocalActivityManagerFragment.

    5. And LocalActivityManager contains MyMapActivity activity in it.

    Example implementation: https://github.com/inazaruk/map-fragment.


    enter image description here

    As discussed at Google Groups, Peter Doyle built a custom compatibility library supporting Google Maps too. android-support-v4-googlemaps

    However, there’s a downside too:

    Currently, one downside is that ALL classes extending FragmentActivity are MapActivitys. Its possible to make a separate class (i.e. FragmentMapActivity), but it requires some refactoring of the FragmentActivity code.

    Just to clarify the answer. I tried the approach suggested by inazaruk and ChristophK. Actually you can run any activity in a fragment – not just google maps. Here is the code which implements google map activity as a fragment thanks to inazaruk and ChristophK.

    import com.actionbarsherlock.app.SherlockFragment;
    import android.view.Window;
    
    import android.app.LocalActivityManager;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class MapFragment extends SherlockFragment {
        private static final String KEY_STATE_BUNDLE = "localActivityManagerState";
    
        private LocalActivityManager mLocalActivityManager;
    
        protected LocalActivityManager getLocalActivityManager() {
            return mLocalActivityManager;
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            Bundle state = null;
            if (savedInstanceState != null) {
                state = savedInstanceState.getBundle(KEY_STATE_BUNDLE);
            }
    
            mLocalActivityManager = new LocalActivityManager(getActivity(), true);
            mLocalActivityManager.dispatchCreate(state);
        }
    
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
                //This is where you specify you activity class
            Intent i = new Intent(getActivity(), GMapActivity.class); 
            Window w = mLocalActivityManager.startActivity("tag", i); 
            View currentView=w.getDecorView(); 
            currentView.setVisibility(View.VISIBLE); 
            currentView.setFocusableInTouchMode(true); 
            ((ViewGroup) currentView).setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            return currentView;
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putBundle(KEY_STATE_BUNDLE,
                    mLocalActivityManager.saveInstanceState());
        }
    
        @Override
        public void onResume() {
            super.onResume();
            mLocalActivityManager.dispatchResume();
        }
    
        @Override
        public void onPause() {
            super.onPause();
            mLocalActivityManager.dispatchPause(getActivity().isFinishing());
        }
    
        @Override
        public void onStop() {
            super.onStop();
            mLocalActivityManager.dispatchStop();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            mLocalActivityManager.dispatchDestroy(getActivity().isFinishing());
        }
    }
    

    Great news from Google on this. They are releasing today a new Google Maps API, with indoor maps and MapFragment.

    With this new API, adding a map to your Activity is as simple as:
    
    <fragment
      android:id="@+id/map"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      class="com.google.android.gms.maps.MapFragment" />
    

    The Google Maps API is not part of the AOSP. As long as no Googler responds it is barely possible to tell if there will be a MapFragment in the future.

    A possible limited alternative is to use a WebViewFragment and abuse it to load up a custom maps.google.com URL.

    Hm too bad that Google has not responded yet. FWIW if you really need to do this I found no other way than:

    Have the Tab Managing Activity inherit from MapActivity, create the MapView in there programmatically, have the mapfragment.xml contain a ViewGroup and add the MapView to the ViewGroup using

    ((ViewGroup) getFragmentManager().findFragmentById(R.id.finder_map_fragment).getView()).addView(mapView);;
    

    Clearly this goes strongly against the idea that fragments are ment to be self-contained but …

    Here’s a MonoDroid (Mono for Android) version of a very-simplified MapFragment:

    public class MapFragment : Fragment
    {
        // FOLLOW http://stackoverflow.com/questions/5109336/mapview-in-a-fragment-honeycomb
        private static  String KEY_STATE_BUNDLE = "localActivityManagerState";
    
        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
    
            Bundle state = null;
            if (savedInstanceState != null) {
                state = savedInstanceState.GetBundle(KEY_STATE_BUNDLE);
            }
            mLocalActivityManager = new LocalActivityManager(Activity, true);
            mLocalActivityManager.DispatchCreate(state);
        }
    
        public override Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            //This is where you specify you activity class
            Intent i = new Intent(Activity, typeof(SteamLocationMapActivity)); 
            Window w = mLocalActivityManager.StartActivity("tag", i); 
            View currentView=w.DecorView; 
            currentView.Visibility = ViewStates.Visible; 
            currentView.FocusableInTouchMode = true; 
            ((ViewGroup) currentView).DescendantFocusability = DescendantFocusability.AfterDescendants;
            return currentView;
        }
    
        private LocalActivityManager mLocalActivityManager;
        protected LocalActivityManager GetLocalActivityManager() {
            return mLocalActivityManager;
        }   
    
    
        public override void OnSaveInstanceState(Bundle outState)
        {
            base.OnSaveInstanceState(outState);
            outState.PutBundle(KEY_STATE_BUNDLE,mLocalActivityManager.SaveInstanceState());
        }
    
        public override void OnResume()
        {
            base.OnResume();
            mLocalActivityManager.DispatchResume();
    
        }
    
        public override void OnPause()
        {
            base.OnPause();
            mLocalActivityManager.DispatchPause(Activity.IsFinishing);
        }
    
        public override void OnStop()
        {
            base.OnStop();
            mLocalActivityManager.DispatchStop();
        }
    }
    

    This solves my issue in adding MapView in Fragments.
    https://github.com/petedoyle/android-support-v4-googlemaps

    With the new version of ABS 4.0, there is no suppport for MapFragmentActivity, here is a good solution for having a mapview in a Fragment!

    https://xrigau.wordpress.com/2012/03/22/howto-actionbarsherlock-mapfragment-listfragment/#comment-21

    May I get the solution:

    1. create class TempFragmentActivity extends MapActivity
    2. there is a MapView object inside TempFragmentActivity(like normal define in xml)
    3. remove this MapView object form parent(LinearLayout)(void later exception)
    4. keep this MapView object in somewhere(ex: static member of TempFragmentActivity)
    5. in your Fragment , add this MapView object using code(do not define in xml) into some LinearLayout

    I wrote a little library, mashing up the LocalActivityManager-based solutions to the MapFragment problem (also includes an example app showing various usage situations):

    https://github.com/coreform/android-tandemactivities

    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.