11

I have added a map fragment (API v2) to my app with the map covering the whole screen and a semi-transparent actionbar on top. The activity uses a theme with android:windowActionBarOverlay set to true.

I have also enabled the "MyLocationButton" on the map, but since the map covers the full height of the screen, the button is covered by the action bar.

enter image description here

How can I make the map fragment draw the location button below the action bar or at the bottom of the screen instead?

machtnix
  • 908
  • 2
  • 8
  • 18

5 Answers5

6

Instead of creating your own button, just move the build in button according to the action bar size.
This code works for me and the button is just where the button should be (like in google maps):

// Gets the my location button
View myLocationButton = getSherlockActivity().findViewById(R.id.MainContainer).findViewById(2); 

// Checks if we found the my location button        
if (myLocationButton != null){
        int actionBarHeight = 0;
        TypedValue tv = new TypedValue();

           // Checks if the os version has actionbar in it or not
           if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
              if (getSherlockActivity().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
                actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
           }
           // Before the action bar was added to the api
           else if(getSherlockActivity().getTheme().resolveAttribute(com.actionbarsherlock.R.attr.actionBarSize, tv, true)){
                actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
           }

        // Sets the margin of the button
        ViewGroup.MarginLayoutParams marginParams = new ViewGroup.MarginLayoutParams(myLocationButton.getLayoutParams());
        marginParams.setMargins(0, actionBarHeight + 20, 20, 0);
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(marginParams);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
        myLocationButton.setLayoutParams(layoutParams);
}


Just put this code in the onActivityCreated (if you will put it in the onCreateOptionsMenu, it will not support version before 3.0 - because the life cycle there is different.
Another thing, the "R.id.MainContainer" is the container of the map fragment.

I'm using ActionBar Sherlock, but it will work also for regular action bar with a few modifications..

Or Kazaz
  • 549
  • 1
  • 9
  • 16
  • Please try this code and if it will work for you mark it as the answer so may other people will use it. It works for me.. – Or Kazaz Apr 07 '13 at 22:00
  • It seems like bad practice to me to have that magical number 2 listed in your findViewById call in the second line of your example code. You don't include an explanation as to where you found that number and whether or not we can count on that number not to change, especially if it's not an ID that Google has documented. If they decide to change the ID, then you'll be broken. – Steven Apr 08 '13 at 17:44
  • The ID is from the hierarchyviewer.. And I agree, if Google will change it you also need to change it. But you control your release and if Google update their google-play-services, you will need to implement it before release it. In the worst case if they will change it, check for the new ID in the hierarchyviewer.. And don't forget that this is just until they will fix this: http://code.google.com/p/gmaps-api-issues/issues/detail?id=4670 – Or Kazaz Apr 08 '13 at 20:19
  • 1
    I think the maps get updated whenever Google decides to push an update of Google Play Services to a user's phone... The Google Play Services we use when we develop are just the method stubs to what is in the Google Play Services that they push to the device in the background. If my understanding of how Google Play Services works is correct, this means they can break you at any time. – Steven Apr 09 '13 at 17:24
3

Below (especially in fixMapControlLocations) i've addressed this with ActionBarSherlock.

Issues I had were on narrow screens, and the split action bar having the wrong offset depending on rotation. The isNarrow check through sherlock lets me know if its narrow.

Another key change is i'm setting the padding of the myLocation's parent's parent view. This picks up all controls inside, and based on hierarchyviewer is how google maps is doing it. The Google attribution logo is on the next parent up the tree in a Surface object. Not looking like that is easily movable, so i'm probably just going to end up loosing the bottom action bar's transparency effect to stay in compliance.

protected void onCreate(Bundle savedInstanceState) {
    getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.map);
    setUpMapIfNeeded();

    getSupportActionBar().setBackgroundDrawable(d);
    getSupportActionBar().setSplitBackgroundDrawable(d);
}

private void setUpMapIfNeeded() {
    // Do a null check to confirm that we have not already instantiated the
    // map.
    if (map == null) {
        // Try to obtain the map from the SupportMapFragment.
        map = ((SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map)).getExtendedMap();

        // Check if we were successful in obtaining the map.
        if (map != null) {
            setUpMap();
        }
    }
}

private void setUpMap() {
    fixMapControlLocations();
    .....
}

private void fixMapControlLocations() {
    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.map);
    int actionBarHeight = 0;
    TypedValue tv = new TypedValue();
    if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
    {
        actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
    }

    View myLocationParent = ((View)mapFragment.getView().findViewById(1).getParent());
    View myLocationParentParent = ((View)myLocationParent.getParent());
    myLocationParentParent.setPadding(0, actionBarHeight, 0, isNarrow()?actionBarHeight:0);
}

public boolean isNarrow() {
    return ResourcesCompat.getResources_getBoolean(getApplicationContext(),
            R.bool.abs__split_action_bar_is_narrow);
}
ruckc
  • 505
  • 1
  • 6
  • 23
3

You can accomplish this with the recently-added GoogleMap.setPadding() method:

map.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);

From the API docs:

This method allows you to define a visible region on the map, to signal to the map that portions of the map around the edges may be obscured, by setting padding on each of the four edges of the map. Map functions will be adapted to the padding. For example, the zoom controls, compass, copyright notices and Google logo will be moved to fit inside the defined region, camera movements will be relative to the center of the visible region, etc.

Also see the description of how padding works in GoogleMap.

Mark Doliner
  • 1,543
  • 15
  • 17
1

This has already been filed as an enhancement (please star it if you haven't already) http://code.google.com/p/gmaps-api-issues/issues/detail?id=4670

As a temporary workaround I have added my own find location button below the actionbar (my map fragment is in a RelativeLayout so I just did alignParentRight and set appropriate margin top).

Then in my onClickHandler I did this:

public void onClickHandler(View target) {
   switch (target.getId()) {
      case R.id.my_fml_btn:         
         mMap.setMyLocationEnabled(true);
         View fmlBtn = mMapWrapper.findViewById(2); //mMapWrapper is my RelativeLayout
         if (fmlBtn != null) fmlBtn.setVisibility(View.INVISIBLE);
         break;
   }
}

I used hierarchyviewer to find the id of the button that was added by the maps api. It just happens to be the 2nd view added to the map (and set to invisible).

You can of course you can fiddle about with LayoutParams to offset this button rather than hide it but this button only appears after you setMyLocationEnabled to true! (in my use case I prefer to let the user decide before firing up the gps)

Damian
  • 8,062
  • 4
  • 42
  • 43
0

Make sure you use ?android:attr/actionBarSize (or ?attr/actionBarSize if you're using ActionBarSherlock) to correctly offset the content of the fragment.

Depending of the effect you're trying to accomplish, either apply this value as margin or padding. I'm guessing that because of the semi-transparant ActionBar, you'll want to try padding, in order to still have the map appear behind it (and keep the see-through effect). I'm just not 100% sure whether padding will actually move the 'Locate me' button down... If not, then probably applying a margin is your only other option.

See here for an example and more details on this attribute.

MH.
  • 45,303
  • 10
  • 103
  • 116
  • That repositions the My Location button correctly, but the transparent actionbar overlay is lost for a solid action bar. The margin attribute is applicable to the fragment parent and fragment itself but the padding attribute only works when set in the parent. Either way, this produces a solid action bar. – qubz Jan 13 '13 at 08:48