Map View in Fragment with Tab Action Bar

7544
11
06-04-2012 12:23 PM
AmelieBernier
New Contributor II
I have an application that uses ActionBarSherlock and Tab navigation with the Android compatibility library. I have 4 fragments that are attached or detached with FragmentTransaction depending on the selected tab.

One of the fragment should display a MapView, but I cannot seem to make it work.  Do you have examples of using a MapView within a fragment, or any tips for implementing this? 


Here is my code. One of the things I tried is to simply retrieve the MapView from the XML Layout, but the app just crashes at startup.

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity; 
import android.os.Bundle;  
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;

public class ActionBarTabsActivity extends SherlockFragmentActivity {  
   
 @Override 
 public void onCreate(Bundle savedInstanceState) {  
  super.onCreate(savedInstanceState); 
  
  ActionBar actionBar = getSupportActionBar();  
  actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
      
  String label1 = getResources().getString(R.string.mapTabLabel);  
  Tab tab = actionBar.newTab();  
  tab.setText(label1);  
  TabListener<MapFragment> tl = new TabListener<MapFragment>(this,  
    label1, MapFragment.class);  
  tab.setTabListener(tl);    
  actionBar.addTab(tab); 
  
  String label2 = getResources().getString(R.string.listTabLabel);  
  tab = actionBar.newTab();  
  tab.setText(label2);  
  TabListener<ParkListFragment> tl2 = new TabListener<ParkListFragment>(this,  
    label2, ParkListFragment.class);  
  tab.setTabListener(tl2);  
  actionBar.addTab(tab);  
  
  String label3 = getResources().getString(R.string.filtersTabLabel);  
  tab = actionBar.newTab();  
  tab.setText(label3);  
  TabListener<FilterListFragment> tl3 = new TabListener<FilterListFragment>(this,  
    label3, FilterListFragment.class);  
  tab.setTabListener(tl3);  
  actionBar.addTab(tab);
  
  String label4 = getResources().getString(R.string.aboutTabLabel);  
  tab = actionBar.newTab();  
  tab.setText(label4);  
  TabListener<AboutFragment> tl4 = new TabListener<AboutFragment>(this,  
    label4, AboutFragment.class);  
  tab.setTabListener(tl4);  
  actionBar.addTab(tab);
  
  // Restore the current activated tab index
  if (savedInstanceState != null) 
  {         
   actionBar.setSelectedNavigationItem(savedInstanceState.getInt("tab_index", 0));     
  }   
 }  

 private class TabListener<T extends Fragment> implements ActionBar.TabListener {  
  
  private Fragment mFragment;  
  private final SherlockFragmentActivity mActivity;  
  private final String mTag;  
  private final Class<T> mClass;  

  /**  
   * Constructor used each time a new tab is created.  
   *  
   * @param activity  
   *            The host Activity, used to instantiate the fragment  
   * @param tag  
   *            The identifier tag for the fragment  
   * @param clz  
   *            The fragment's Class, used to instantiate the fragment  
   */ 

  public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {  
   
   mActivity = activity;  
   mTag = tag;  
   mClass = clz;  
  }  

  public void onTabSelected(Tab tab, FragmentTransaction ft) {  
   
   mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(mTag); 
   // Check if the fragment is already initialized  
   if (mFragment == null) {  
    // If not, instantiate and add it to the activity  
    mFragment = Fragment.instantiate(mActivity, mClass.getName());  
    ft.add(android.R.id.content, mFragment, mTag);  
   } else {  
    // If it exists, simply attach it in order to show it  
    ft.attach(mFragment);  
   }  
  }  

  public void onTabUnselected(Tab tab, FragmentTransaction ft) {  

   if (mFragment != null) {  
    // Detach the fragment, because another one is being attached  
    ft.detach(mFragment);  
   }  
  }  

  public void onTabReselected(Tab tab, FragmentTransaction ft) {  

   // User selected the already selected tab. Usually do nothing.  
  }  
 } 
 
 @Override 
 protected void onSaveInstanceState(Bundle outState) 
 {     
  super.onSaveInstanceState(outState);
  // Save the current activated tab index
  outState.putInt("tab_index", getSupportActionBar().getSelectedNavigationIndex());   
 } 
}


import com.esri.android.map.MapView;
import com.esri.android.map.ags.ArcGISTiledMapServiceLayer;
import android.os.Bundle;  
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
import android.widget.LinearLayout;  

public class MapFragment extends Fragment {  
 
 MapView map;
 
 @Override 
 public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  Bundle savedInstanceState) {  
  
  super.onCreate(savedInstanceState);
  
  View layout = inflater.inflate(R.layout.map_test, container, false);
      
  // Retrieve the map and initial extent from XML layout
  map = (MapView) layout.findViewById(R.id.map_view);
 
  // Add dynamic layer to MapView
  map.addLayer(new ArcGISTiledMapServiceLayer(
   "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"));
  
  return (LinearLayout) layout;  
 }  
}


[HTML]<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"> 
 
<!-- MapView layout and initial extent -->
<com.esri.android.map.MapView xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/map_view"
  initExtent="490969 5452639 508730 5473334" 
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
</com.esri.android.map.MapView>

</LinearLayout> [/HTML]
0 Kudos
11 Replies
AndyGup
Esri Regular Contributor
Amelie, I haven't used Fragments much, but...logcat should give you some hints on what and where the problem is. I suggest posting the crash or FATAL error portion from logcat.

-Andy
0 Kudos
AmelieBernier
New Contributor II
Hi Andy,

It seems that I had a problem with the Internet connection at job which caused the app to crash when adding the layers to the mapview. But there is still a problem with the MapView...

Now the app starts correctly and the MapView is loaded. The map appears in the first tab. However, when I try to switch to a new tab, the app crashes. When there is no MapView in the Fragment (only a simple TextView), everything works fine.

Here is the LogCat after application startup :

06-05 13:24:47.592: W/dalvikvm(10355): VFY: unable to resolve virtual method 4533: Lcom/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout;.onHoverEvent (Landroid/view/MotionEvent;)Z
06-05 13:24:47.602: W/dalvikvm(10355): VFY: unable to resolve virtual method 3174: Landroid/widget/FrameLayout;.getAlpha ()F
06-05 13:24:47.602: W/dalvikvm(10355): VFY: unable to resolve virtual method 3175: Landroid/widget/FrameLayout;.getTranslationY ()F
06-05 13:24:47.602: W/dalvikvm(10355): VFY: unable to resolve virtual method 3182: Landroid/widget/FrameLayout;.setAlpha (F)V
06-05 13:24:47.602: W/dalvikvm(10355): VFY: unable to resolve virtual method 3191: Landroid/widget/FrameLayout;.setTranslationY (F)V
06-05 13:24:47.622: W/dalvikvm(10355): VFY: unable to resolve virtual method 280: Landroid/content/pm/PackageManager;.getActivityLogo (Landroid/content/ComponentName;)Landroid/graphics/drawable/Drawable;
06-05 13:24:47.622: W/dalvikvm(10355): VFY: unable to resolve virtual method 277: Landroid/content/pm/ApplicationInfo;.loadLogo (Landroid/content/pm/PackageManager;)Landroid/graphics/drawable/Drawable;
06-05 13:24:47.682: W/dalvikvm(10355): VFY: unable to resolve virtual method 2884: Landroid/view/ViewGroup;.getAlpha ()F
06-05 13:24:47.682: W/dalvikvm(10355): VFY: unable to resolve virtual method 2890: Landroid/view/ViewGroup;.getTranslationX ()F
06-05 13:24:47.682: W/dalvikvm(10355): VFY: unable to resolve virtual method 2891: Landroid/view/ViewGroup;.getTranslationY ()F
06-05 13:24:47.682: W/dalvikvm(10355): VFY: unable to resolve virtual method 2907: Landroid/view/ViewGroup;.setAlpha (F)V
06-05 13:24:47.682: W/dalvikvm(10355): VFY: unable to resolve virtual method 2911: Landroid/view/ViewGroup;.setTranslationX (F)V
06-05 13:24:47.682: W/dalvikvm(10355): VFY: unable to resolve virtual method 2912: Landroid/view/ViewGroup;.setTranslationY (F)V
06-05 13:24:47.692: W/dalvikvm(10355): VFY: unable to resolve virtual method 5393: Lcom/actionbarsherlock/internal/widget/ActionBarView$HomeView;.onHoverEvent (Landroid/view/MotionEvent;)Z
06-05 13:24:47.692: W/dalvikvm(10355): VFY: unable to resolve virtual method 3180: Landroid/widget/FrameLayout;.onPopulateAccessibilityEvent (Landroid/view/accessibility/AccessibilityEvent;)V
06-05 13:24:47.722: W/dalvikvm(10355): VFY: unable to resolve virtual method 3206: Landroid/widget/HorizontalScrollView;.getAlpha ()F
06-05 13:24:47.722: W/dalvikvm(10355): VFY: unable to resolve virtual method 3209: Landroid/widget/HorizontalScrollView;.setAlpha (F)V
06-05 13:24:47.722: W/dalvikvm(10355): VFY: unable to resolve direct method 3259: Landroid/widget/LinearLayout;.<init> (Landroid/content/Context;Landroid/util/AttributeSet;I)V
06-05 13:24:47.732: W/dalvikvm(10355): VFY: unable to resolve virtual method 3265: Landroid/widget/LinearLayout;.getAlpha ()F
06-05 13:24:47.732: W/dalvikvm(10355): VFY: unable to resolve virtual method 3270: Landroid/widget/LinearLayout;.getTranslationX ()F
06-05 13:24:47.732: W/dalvikvm(10355): VFY: unable to resolve virtual method 3280: Landroid/widget/LinearLayout;.setAlpha (F)V
06-05 13:24:47.732: W/dalvikvm(10355): VFY: unable to resolve virtual method 3290: Landroid/widget/LinearLayout;.setTranslationX (F)V
06-05 13:24:47.742: W/dalvikvm(10355): VFY: unable to resolve static field 5851 (ROOT) in Ljava/util/Locale;
06-05 13:24:48.772: D/GLMapCore(10355): Thread 10366 = Pulse
06-05 13:24:48.772: D/GLMapCore(10355): Thread 10365 = Dispatch
06-05 13:24:48.772: D/GLMapCore(10355): Thread 10364 = Worker 1
06-05 13:24:48.772: D/GLMapCore(10355): Thread 10363 = Worker 0
06-05 13:24:48.932: W/dalvikvm(10355): VFY: unable to resolve virtual method 2808: Landroid/view/View;.jumpDrawablesToCurrentState ()V
06-05 13:24:50.582: D/GLMapCore(10355): Map spatial reference set to 102100

And these two lines appear when attemptimg to switch tab:

06-05 13:25:40.282: I/GLMapCore(10355): TexturesManager::releaseGraphicsHardwareResources
06-05 13:25:40.282: E/libEGL(10355): call to OpenGL ES API with no current context (logged once per thread)
0 Kudos
AndyGup
Esri Regular Contributor
Amelie, just reiterating that I haven't worked much with Fragments, however I have seen similar errors to the last two lines you posted:

06-05 13:25:40.282: I/GLMapCore(10355): TexturesManager::releaseGraphicsHardwareResources
06-05 13:25:40.282: E/libEGL(10355): call to OpenGL ES API with no current context (logged once per thread)

The only thing I can think of is make sure the AndroidManifest is configured properly. There may be a property that is needed. Hopefully someone else who has used Fragments can jump in. 

http://developer.android.com/guide/topics/manifest/activity-element.html


-Andy
0 Kudos
AmelieBernier
New Contributor II
Okay so I may have made some progress on this...

When I replace ft.attach and ft.detach in the TabListener of ActionBarTabsActivity with ft.show and ft.hide, navigating through the tabs works correctly with the MapView.

However, when I rotate the phone, the app crashes with the same error :

call to OpenGL ES API with no current context (logged once per thread)
0 Kudos
AndyGup
Esri Regular Contributor
Related to the app crashing when you change orientation, try adding the following line to your AndroidManifest android:configChanges="orientation", for example:

        <activity android:name=".AGSLocatorActivity"
                  android:label="@string/app_name"
                   android:configChanges="orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.esri.arcgis.android.agslocator.WebviewActivity">
        </activity> 


-Andy
0 Kudos
AmelieBernier
New Contributor II
Related to the app crashing when you change orientation, try adding the following line to your AndroidManifest android:configChanges="orientation"


Hi Andy,

I tried this and now the app doesn't crash when the device is rotated. However there is still a problem : once the phone is in landscape mode, if I select a new Tab I get the following error :

06-06 08:58:53.221: E/ResourceType(14078): Style contains key with bad entry: 0x01000000

Then if I switch back to portrait mode, everything works fine again.

Thanks for your help.
0 Kudos
AndyGup
Esri Regular Contributor
The Style problem is related to ActionBarSherlock. Here's a post that might help: http://stackoverflow.com/questions/9686818/actionbarsherlock-style-contains-key-with-bad-entry

-Andy
0 Kudos
AmelieBernier
New Contributor II
The Style problem is related to ActionBarSherlock. Here's a post that might help: http://stackoverflow.com/questions/9686818/actionbarsherlock-style-contains-key-with-bad-entry

-Andy


This error does not make the app crash. When I get the message in landscape mode, I can't navigate through the tabs (like if the Activity is frozen). However when I switch back to portrait mode, the app continues running just fine and I can still switch between tabs.

I saw this post, and from what it says and from what I read elsewhere, this "error" is more of a warning and does not stop the app from running.

I think that the problem is with android:configChanges="orientation". It prevents the activity being recreated when the orientation of the device is changed, so that may be why the activity is unresponsive? Should I do something specific in onConfigurationChanged?
0 Kudos
AndyGup
Esri Regular Contributor
I think that the problem is with android:configChanges="orientation". It prevents the activity being recreated when the orientation of the device is changed, so that may be why the activity is unresponsive? Should I do something specific in onConfigurationChanged?


Good question, I'm not certain how this problem relates to ActionBarSherlock, or what changes to make to onConfigurationChanged. 

On our end, we are investigating the current requirement, in certain circumstances. for the inclusion of android:configChanges="orientation" and android:configChanges="orientation|screenSize" (for versions of Android OS 3.2 and higher) in the AndroidManifest.

I do know that if you manually handle onConfigurationChanged you also have to reset any element. Here's a snippet from the Android Help doc:
Remember: When you declare your activity to handle a configuration change, you are responsible for resetting any elements for which you provide alternatives. If you declare your activity to handle the orientation change and have images that should change between landscape and portrait, you must re-assign each resource to each element during onConfigurationChanged().

-Andy
0 Kudos