MapFragment in Action Bar Tabs
Asked Answered
O

2

9

I am trying to build an app that will implement Action Bar tabs. One of the tabs should contain a MapFragment.

How can I implement an action bar with tabs, under one of which is a map Fragment?

Can you help me with how to proceed with this?

Here is what I have so far :

main class

package com.nfc.demo;

import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;

public class NFCDemoActivity extends Activity {

  Tab selectedTab = null;

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

    ActionBar bar = getActionBar();
    bar.setDisplayShowHomeEnabled(false);
    bar.setDisplayShowTitleEnabled(false);

    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    bar.setCustomView(R.layout.main);

    ActionBar.Tab mapTab = bar.newTab().setText("Map");
    ActionBar.Tab settingsTab = bar.newTab().setText("Settings");
    ActionBar.Tab aboutTab = bar.newTab().setText("About");

    MapFragment mapFragment = new MapFragment();
    SettingsFragment settingsFragment = new SettingsFragment();
    AboutFragment aboutFragment = new AboutFragment();

    mapTab.setTabListener(new TabListener(mapFragment));
    settingsTab.setTabListener(new TabListener(settingsFragment));
    aboutTab.setTabListener(new TabListener(aboutFragment));

    Tab selectedTab = (Tab) getLastNonConfigurationInstance();

    if (selectedTab == null) {
      bar.addTab(mapTab, false);
      bar.addTab(settingsTab, false);
      bar.addTab(aboutTab, true);
    }

    setContentView(R.layout.main);

  }

  public Object onRetainNonConfigurationInstance() {
    return selectedTab;
  }

  protected boolean isRouteDisplayed() {
      return false;
  }

  protected class TabListener implements ActionBar.TabListener {
    private Fragment fragment;

    public TabListener(Fragment fragment) {
        this.fragment = fragment;
    }

    public void onTabSelected(Tab tab, FragmentTransaction fragmentTransaction) {
        fragmentTransaction.replace(R.id.mainFragment, this.fragment, null);
        selectedTab = tab;
    }

    public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) {
        fragmentTransaction.remove(this.fragment);
    }

    public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) {
        //do nothing
    }
  }
}

The Fragment classes are all just returning an inflater with an .xml layout.

XML Layouts :

main.xml ( map should be on this XML file )

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

</LinearLayout>

settings.xml AND about.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/textView123"
        android:text="asdfg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

But adding the MapFragment thows a error:

Error inflating class fragment 
error caused by java.lang.IllegalArgumentException: 
Binary XML file line #2: Duplicate id 0x7f040005, tag null, or parent id 0x1020002 with another fragment for com.google.android.gms.maps.MapFragment 12-28 21:14:07.991: E/AndroidRuntime(26189): at android.app.Activity.onCreateView(Activity.java:4722)

I've been trying to figure out how to proceed for a couple of days but I am really confused. Any help/tips would be greatly appreciated.

Also, what about getLastNonConfigurationInstance()? It is deprecated.

Oto answered 28/12, 2012 at 19:25 Comment(3)
sorry, I thought my question was clear. I edited it, but let me know if it needs further improvement.Oto
Well, your overall question is clear, but then you hand us a bunch of code and do not explain what the problem is with that code. Having a MapFragment be in an action bar tab is no different than having any other fragment be in an action bar tab.Eustasius
I can't seem to figure out how to do it, though. Once I alter the main.xml and add a fragment I get an Error inflating class fragment error caused by java.lang.IllegalArgumentException: Binary XML file line #2: Duplicate id 0x7f040005, tag null, or parent id 0x1020002 with another fragment for com.google.android.gms.maps.MapFragment 12-28 21:14:07.991: E/AndroidRuntime(26189): at android.app.Activity.onCreateView(Activity.java:4722)Oto
C
17

In the following solution, it is possible to add a GoogleMap to an Action Bar tab/dropdown. The key to doing this lies in correctly setting up your fragment to destroy the MapFragment when switching to another fragment in the Action Bar.

Create an Activity that implements the Action Bar functionality:

  1. Create a project in Eclipse that uses Tabs for the main activity. If you don't do this, proceed to steps 2-5.
  2. Create a class that extends Activity and implements ActionBar.OnNavigationListener.
  3. Create a layout XML file that is a container for your tab fragments when you switch between them.
  4. Implement/override the following method in your Activity class: public boolean onNavigationItemSelected(int position, long id).
  5. In this method, switch between the position object to determine the selected tab and set the fragment to a new instance using the FragmentManager like this: getFragmentManager().beginTransaction().replace(R.id.container, fragment).commit().

Create a fragment that holds the map:

  1. Create a class that extends Fragment to use as your tab's fragment. Read [1] to better understand the MapFragment.
  2. Create a layout XML file that contains a fragment element (as seen in [1]).Use the XML in that page to create a layout XML file and use it in your fragment class.
  3. Inflate that layout XML file in your fragment class by overriding onCreateView.
  4. Your app should now display a map in the tab that uses your fragment class, however, switching to another tab and back to the map tab will result in a duplicate view ID. To overcome this, go on to the next step.
  5. In your fragment class, override the following method to specifically destroy the underlying GoogleMap object so that it can be recreated when the map tab loads your fragment class again:

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        MapFragment f = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
        if (f != null) 
            getFragmentManager().beginTransaction().remove(f).commit();
    }
Chemosynthesis answered 22/1, 2013 at 19:58 Comment(4)
Now I'm using: if (f != null && f.isResumed()) or I'm getting Exceptions while closing my app pressing back button. TYDuro
this works, but if i rotate my screen, my app crashes with this exception: Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceStateYingyingkow
@Matt: I dont think this is a clean approach, since in this situation we have a nested fragment (MapFragment within Tab- Fragment) and it is still not supported in a static way using layouts. Author: either use MapView as a replacement for MapFragment or .hide() / .show() the Fragment with the map instead of .replace() / .remove()-ing it (via FragmentTransaction). You may also want to try support-library (rev 11 and up) which provide programmatic nested fragments support.Ferbam
I finally got it working smoothly with my custom fragment and plain MapView, see code here https://mcmap.net/q/94873/-google-map-android-api-v2-googlemap-is-nullFerbam
V
-1

Not sure if you already resolved it or not. You have to add google play services as library project to make it work. First I tried to add the jar file, but that didn't work.

Vaudeville answered 17/1, 2013 at 21:53 Comment(1)
If google play services is not set up properly your map would not load.Tinsel

© 2022 - 2024 — McMap. All rights reserved.