4

I am working in a new Android project.

The first activity is using a slider menu and fragments. On the first fragment there is a list view (PrimaryFragmentDormir.java). After selecting one of the rows, a new activity is launched. This last activity uses three tabs, to show different information about the selected row object. The listview is loaded from remote JSON files. The first tab shows details about the selected object, the second tab should show a map.

I have followed the official documentation to setup all the needed to include Google Maps on my app, at least I think so. When the user selects the second tab, a blank screen is shown with the Google logo at the left bottom side.

Here is the Android Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.solinpromex.elpasojuarezexperience" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>

    <uses-library android:name="com.google.android.maps"/>


    <application
        android:name=".app.AppController"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="..hidden here..." />
        <activity
            android:name=".Inicio"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
        </activity>
        <activity
            android:name=".Detalle_Hotel"
            android:label="@string/title_activity_detalle__hotel" >
        </activity>



    </application>

</manifest>

Here the main activity that hosts the tabs:

import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

public class Detalle_Hotel extends AppCompatActivity {
    // Declaring Your View and Variables

    public String nombre_hotel_recibido, foto_hotel_recibido, descripcion_hotel_recibido,direccion_hotel_recibido,web_hotel,tel_hotel,tel_reservas,zona_hotel,facebook_hotel,twitter_hotel;
    private int hotel_num_estrellas, id_hotel;
    private double calificacion_hotel,latitud_hotel,longitud_hotel;

    Toolbar toolbar;
    ViewPager pager;
    ViewPagerAdapter adapter;
    SlidingTabLayout tabs;
    CharSequence Titles[]={"Info","Mapa","Opinión"};
    int Numboftabs =3;

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

        nombre_hotel_recibido = getIntent().getStringExtra("nombre_hotel");
        direccion_hotel_recibido = getIntent().getStringExtra("direccion_hotel");
        descripcion_hotel_recibido = getIntent().getStringExtra("descripcion_hotel");
        foto_hotel_recibido = getIntent().getStringExtra("foto_hotel");
        hotel_num_estrellas = getIntent().getIntExtra("num_estrellas",0);
        setTitle(nombre_hotel_recibido);

        // Creating The Toolbar and setting it as the Toolbar for the activity

        toolbar = (Toolbar) findViewById(R.id.tool_bar);
        setSupportActionBar(toolbar);


        // Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
        adapter =  new ViewPagerAdapter(getSupportFragmentManager(),Titles,Numboftabs);

        // Assigning ViewPager View and setting the adapter
        pager = (ViewPager) findViewById(R.id.pager);
        pager.setAdapter(adapter);

        // Assiging the Sliding Tab Layout View
        tabs = (SlidingTabLayout) findViewById(R.id.tabs);
        tabs.setDistributeEvenly(true); // To make the Tabs Fixed set this true, This makes the tabs Space Evenly in Available width

        // Setting Custom Color for the Scroll bar indicator of the Tab View
        tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
            @Override
            public int getIndicatorColor(int position) {
                return getResources().getColor(R.color.rojomodesto);
            }
        });

        // Setting the ViewPager For the SlidingTabsLayout
        tabs.setViewPager(pager);



    }



}

Here the code for the second tab:

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.maps.GoogleMap;

import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import com.google.android.gms.maps.GoogleMap;

import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;



public class Tab2 extends Fragment {

    private GoogleMap map;
    static final LatLng HAMBURG = new LatLng(53.558, 9.927);
    static final LatLng KIEL = new LatLng(53.551, 9.993);

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.tab_2, container, false);

        return v;


    }


    @Override
    public void onActivityCreated(Bundle state) {
        super.onActivityCreated(state);


        map = ((MapFragment) getActivity().getFragmentManager().findFragmentById(R.id.map))
                .getMap();

        if (map!=null){
            Marker hamburg = map.addMarker(new MarkerOptions().position(HAMBURG)
                    .title("Hamburg"));
            Marker kiel = map.addMarker(new MarkerOptions()
                    .position(KIEL)
                    .title("Kiel")
                    .snippet("Kiel is cool")
                    .icon(BitmapDescriptorFactory
                            .fromResource(R.drawable.res)));
        }

    }

}

I have checked out the API key and it is OK. Thank you for your support.

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
mvasco
  • 4,965
  • 7
  • 59
  • 120

1 Answers1

8

The problem is that when you call getActivity().getFragmentManager(), you're getting the Activity's Fragment Manager.

Since you're using a nested MapFragment inside of a Fragment, you need to use getChildFragmentManager(): For example:

GoogleMap mapFragment = ((MapFragment) getChildFragmentManager()
            .findFragmentById(R.id.map)).getMap();

So, replace this:

map = ((MapFragment) getActivity().getFragmentManager().findFragmentById(R.id.map))
                .getMap();

With this:

map = ((MapFragment) getChildFragmentManager().findFragmentById(R.id.map))
                .getMap();

However, while that would work, you would be better off having your Fragment extend MapFragment or SupportMapFragment, that way you wouldn't need to have nested Fragments, and you would avoid the potential of running into this problem.

Solution using TabLayout from the support library:

As ActionBar Tabs are deprecated, this is now the best way to implement tabs with a ViewPager. Notice this is done without use of a nested SupportMapFragment, and instead has the map Fragment extent SupportMapFragment:

MainActivity imports:

import android.content.Context;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.app.FragmentManager;
import android.view.View;
import android.widget.TextView;

MainActivity class:

public class MainActivity extends AppCompatActivity {

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Get the ViewPager and set it's PagerAdapter so that it can display items
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        PagerAdapter pagerAdapter =
                new PagerAdapter(getSupportFragmentManager(), MainActivity.this);
        viewPager.setAdapter(pagerAdapter);

        // Give the TabLayout the ViewPager
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        tabLayout.setupWithViewPager(viewPager);

        // Iterate over all tabs and set the custom view
        for (int i = 0; i < tabLayout.getTabCount(); i++) {
            TabLayout.Tab tab = tabLayout.getTabAt(i);
            tab.setCustomView(pagerAdapter.getTabView(i));
        }
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    class PagerAdapter extends FragmentPagerAdapter {

        String tabTitles[] = new String[] { "Tab One", "Tab Two", "Tab Three", };
        Context context;

        public PagerAdapter(FragmentManager fm, Context context) {
            super(fm);
            this.context = context;
        }

        @Override
        public int getCount() {
            return tabTitles.length;
        }

        @Override
        public Fragment getItem(int position) {

            switch (position) {
                case 0:
                    return new BlankFragment();
                case 1:
                    return new BlankFragment();
                case 2:
                    return new MapFragment();
            }

            return null;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            // Generate title based on item position
            return tabTitles[position];
        }

        public View getTabView(int position) {
            View tab = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_tab, null);
            TextView tv = (TextView) tab.findViewById(R.id.custom_text);
            tv.setText(tabTitles[position]);
            return tab;
        }

    }
}

The MapFragment, notice that it directly extends SupportMapFragment instead of having a nested SupportMapFragment inside it:

import android.util.Log;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapFragment extends SupportMapFragment implements
        OnMapReadyCallback {

    private final LatLng HAMBURG = new LatLng(53.558, 9.927);
    private final LatLng KIEL = new LatLng(53.551, 9.993);

    private static final String ARG_SECTION_NUMBER = "section_number";

    private GoogleMap mMap;
    private Marker marker;

    public MapFragment() {
    }

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

        Log.d("MyMap", "onResume");
        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mMap == null) {

            Log.d("MyMap", "setUpMapIfNeeded");

            getMapAsync(this);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        Log.d("MyMap", "onMapReady");
        mMap = googleMap;
        setUpMap();
    }

    private void setUpMap() {

        mMap.setMyLocationEnabled(true);
        mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
        mMap.getUiSettings().setMapToolbarEnabled(false);


        mMap.setMyLocationEnabled(true);
        mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
        mMap.getUiSettings().setMapToolbarEnabled(false);

        Marker hamburg = mMap.addMarker(new MarkerOptions().position(HAMBURG)
                .title("Hamburg"));
        Marker kiel = mMap.addMarker(new MarkerOptions()
                .position(KIEL)
                .title("Kiel")
                .snippet("Kiel is cool")
                .icon(BitmapDescriptorFactory
                        .fromResource(R.mipmap.ic_launcher)));

        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(HAMBURG, 15));

        mMap.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);
    }
}

layout xml for the Activity:

<RelativeLayout
    android:id="@+id/main_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:background="?attr/colorPrimary"
        android:elevation="6dp"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        app:tabMode="fixed"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:background="?attr/colorPrimary"
        android:elevation="6dp"
        app:tabTextColor="#d3d3d3"
        app:tabSelectedTextColor="#ffffff"
        app:tabIndicatorColor="#ff00ff"
        android:minHeight="?attr/actionBarSize"
        />

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/tab_layout"/>

</RelativeLayout>

custom_tab.xml, which is the layout for each tab:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/custom_text"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:background="?attr/selectableItemBackground"
        android:gravity="center"
        android:textSize="16dip"
        android:textColor="#ffffff"
        android:singleLine="true"
        />
</LinearLayout>

Gradle:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
    compile 'com.google.android.gms:play-services:7.0.0'
}

Result:

enter image description here

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • Thank you Daniel, your answer is a master class for me... I don't know if I am able to understand all the knowledge in your answer. At first, I am trying to implement your first proposal with getChildFragmentManager, but how? – mvasco Sep 15 '15 at 06:33
  • @mvasco No problem, glad to help. I just updated the answer with one small change that will probably make it work for you. Also, note that in the second example, the ActionBar tabs are deprecated, so this should just be used as a reference for how to set up your Fragment. These days I would use a TabLayout instead of ActionBarTabs (it looks like your current tab method is good as well). – Daniel Nugent Sep 15 '15 at 06:42
  • if I implement your proposal, method getChildFragmentManager() cannot be resolved...am I doing it ok putting all inside onActivityCreated() method union getChildFragmentManager()??? – mvasco Sep 15 '15 at 06:56
  • Thank you Daniel, tomorrow I will take a better look to your answer, but I am sure your answer is exactly what I need. – mvasco Sep 15 '15 at 07:07
  • @mvasco one more thing, make sure that your MapFragment xml is inside tab_2.xml. Also, make sure that you have enabled Google maps V2 in the Google developer console! – Daniel Nugent Sep 15 '15 at 14:00
  • Thank you Daniel, I cannot find there Google maps V2, there is only Google Maps Android API...is that right? – mvasco Sep 15 '15 at 14:05
  • Also make sure that you import the support v4 version of FragmentManager! – Daniel Nugent Sep 15 '15 at 14:21
  • Daniel, I guess there is an issue related to the Google Maps API key. I have tried with the default Map activity from android studio, created a new API key for it, and the same result. I am tired of it...any help would be appreciate. – mvasco Sep 15 '15 at 16:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/89724/discussion-between-daniel-nugent-and-mvasco). – Daniel Nugent Sep 15 '15 at 17:53
  • Daniel I have it, I only needed to uninstall the app from the device and then use the link given by the default map activity to create the project in the Developers Console: https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=here sha1 fingerprint%3Bhere package name – mvasco Sep 16 '15 at 01:01
  • Hey @DanielNugent, I have a question, if I want to add the markers after the map is loaded, how do I do it? – Diego Rivera May 09 '16 at 21:01
  • Can you share the project as I am working on the same . – Sumit Ramteke Feb 19 '17 at 09:31