web-dev-qa-db-fra.com

ID dupliqué, balise null ou id parent avec un autre fragment pour com.google.Android.gms.maps.MapFragment

J'ai une application avec trois onglets.

Chaque onglet a son propre fichier .xml de présentation. Le fichier main.xml a son propre fragment de carte. C'est celui qui apparaît lorsque l'application démarre pour la première fois.

Tout fonctionne bien sauf quand je change entre les onglets. Si j'essaie de revenir à l'onglet fragment de carte, j'obtiens cette erreur. Basculer vers et entre d'autres onglets fonctionne très bien.

Quel pourrait être le problème ici?

Ceci est ma classe principale et mon main.xml, ainsi qu'une classe pertinente que j'utilise (vous trouverez également le journal des erreurs en bas)

classe principale

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;
import Android.widget.Toast;

public class NFCDemoActivity extends Activity {

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

        ActionBar bar = getActionBar();
        bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

        bar.addTab(bar
                .newTab()
                .setText("Map")
                .setTabListener(
                        new TabListener<MapFragment>(this, "map",
                                MapFragment.class)));
        bar.addTab(bar
                .newTab()
                .setText("Settings")
                .setTabListener(
                        new TabListener<SettingsFragment>(this, "settings",
                                SettingsFragment.class)));
        bar.addTab(bar
                .newTab()
                .setText("About")
                .setTabListener(
                        new TabListener<AboutFragment>(this, "about",
                                AboutFragment.class)));

        if (savedInstanceState != null) {
            bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
        }
        // setContentView(R.layout.main);

    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
    }

    public static class TabListener<T extends Fragment> implements
            ActionBar.TabListener {
        private final Activity mActivity;
        private final String mTag;
        private final Class<T> mClass;
        private final Bundle mArgs;
        private Fragment mFragment;

        public TabListener(Activity activity, String tag, Class<T> clz) {
            this(activity, tag, clz, null);
        }

        public TabListener(Activity activity, String tag, Class<T> clz,
                Bundle args) {
            mActivity = activity;
            mTag = tag;
            mClass = clz;
            mArgs = args;

            // Check to see if we already have a fragment for this tab,
            // probably from a previously saved state. If so, deactivate
            // it, because our initial state is that a tab isn't shown.
            mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
            if (mFragment != null && !mFragment.isDetached()) {
                FragmentTransaction ft = mActivity.getFragmentManager()
                        .beginTransaction();
                ft.detach(mFragment);
                ft.commit();
            }
        }

        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName(),
                        mArgs);
                ft.add(Android.R.id.content, mFragment, mTag);
            } else {
                ft.attach(mFragment);
            }
        }

        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
            if (mFragment != null) {
                ft.detach(mFragment);
            }
        }

        public void onTabReselected(Tab tab, FragmentTransaction ft) {
            Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT)
                         .show();
        }
    }

}

main.xml

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

    <fragment
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:id="@+id/mapFragment"
        Android:name="com.google.Android.gms.maps.MapFragment"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />
</LinearLayout>

classe pertinente (MapFragment.Java)

package com.nfc.demo;

import Android.app.Fragment;
import Android.os.Bundle;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;

public class MapFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        return inflater.inflate(R.layout.main, container, false);
    }

    public void onDestroy() {
        super.onDestroy();
    }
}

Erreur  

Android.view.InflateException: Binary XML file line #7: 
     Error inflating class fragment
   at Android.view.LayoutInflater.createViewFromTag(LayoutInflater.Java:704)
   at Android.view.LayoutInflater.rInflate(LayoutInflater.Java:746)
   at Android.view.LayoutInflater.inflate(LayoutInflater.Java:489)
   at Android.view.LayoutInflater.inflate(LayoutInflater.Java:396)
   at com.nfc.demo.MapFragment.onCreateView(MapFragment.Java:15)
   at Android.app.Fragment.performCreateView(Fragment.Java:1695)
   at Android.app.FragmentManagerImpl.moveToState(FragmentManager.Java:885)
   at Android.app.FragmentManagerImpl.attachFragment(FragmentManager.Java:1255)
   at Android.app.BackStackRecord.run(BackStackRecord.Java:672)
   at Android.app.FragmentManagerImpl.execPendingActions(FragmentManager.Java:1435)
   at Android.app.FragmentManagerImpl$1.run(FragmentManager.Java:441)
   at Android.os.Handler.handleCallback(Handler.Java:725)
   at Android.os.Handler.dispatchMessage(Handler.Java:92)
   at Android.os.Looper.loop(Looper.Java:137)
   at Android.app.ActivityThread.main(ActivityThread.Java:5039)
   at Java.lang.reflect.Method.invokeNative(Native Method)
   at Java.lang.reflect.Method.invoke(Method.Java:511)
   at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:793)
   at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:560)
   at dalvik.system.NativeStart.main(Native Method)

Caused by: Java.lang.IllegalArgumentException: 
     Binary XML file line #7: Duplicate id 0x7f040005, tag null, or 
     parent id 0xffffffff with another fragment for 
     com.google.Android.gms.maps.MapFragment
   at Android.app.Activity.onCreateView(Activity.Java:4722)
   at Android.view.LayoutInflater.createViewFromTag(LayoutInflater.Java:680)
   ... 19 more
309
hermann

La réponse proposée par Matt fonctionne, mais la carte est recréée et redessinée, ce qui n’est pas toujours souhaitable ... Après de nombreux essais et erreurs, j’ai trouvé une solution qui fonctionne pour moi:

private static View view;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (view != null) {
        ViewGroup parent = (ViewGroup) view.getParent();
        if (parent != null)
            parent.removeView(view);
    }
    try {
        view = inflater.inflate(R.layout.map, container, false);
    } catch (InflateException e) {
        /* map is already there, just return view as it is */
    }
    return view;
}

Pour faire bonne mesure, voici "map.xml" (R.layout.map) avec R.id.mapFragment (Android: id = "@ + id/mapFragment"):

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

    <fragment xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:id="@+id/mapFragment"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        class="com.google.Android.gms.maps.SupportMapFragment" />
</LinearLayout>

J'espère que cela aide, mais je ne peux pas garantir que cela n'aura aucun effet indésirable.

Edit: Il y a eu quelques effets indésirables, tels que le fait de quitter l'application et de la redémarrer. Étant donné que l'application n'est pas nécessairement complètement arrêtée (mais simplement mise en veille en arrière-plan), le code que j'avais soumis précédemment échouerait au redémarrage de l'application. J'ai mis à jour le code pour que quelque chose fonctionne pour moi, à la fois pour entrer et sortir de la carte et pour quitter et redémarrer l'application. Je ne suis pas trop satisfait du bit try-catch, mais cela semble fonctionner assez bien. En regardant la trace de la pile, il m'est apparu que je pouvais simplement vérifier si le fragment de carte se trouvait dans FragmentManager. Le bloc try-catch n'a donc pas besoin d'être mis à jour.

Plus de modifications: Il s'avère que vous avez besoin de cet essai-capture après tout. Vérifier que le fragment de carte ne fonctionnait pas si bien après tout. Blergh.

398
Vidar Wahlberg

Le problème est que ce que vous essayez de faire ne devrait pas être fait. Vous ne devriez pas gonfler des fragments à l'intérieur d'autres fragments. Depuis Android documentation :

Remarque: Vous ne pouvez pas gonfler une mise en page dans un fragment lorsque cette mise en page inclut un <fragment>. Les fragments imbriqués ne sont pris en charge que lorsqu'ils sont ajoutés à un fragment dynamiquement.

Bien que vous puissiez peut-être accomplir la tâche avec les bidouilles présentées ici, je vous suggère fortement de ne pas le faire. Il est impossible de s’assurer que ces hacks gèrent ce que fait chaque nouvel OS Android lorsque vous essayez de gonfler une présentation pour un fragment contenant un autre fragment. 

Le seul moyen pris en charge par Android d’ajouter un fragment à un autre est d’effectuer une transaction à partir du gestionnaire de fragments enfants.

Changez simplement votre mise en page XML dans un conteneur vide (ajoutez un ID si nécessaire): 

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

Ensuite, dans la méthode Fragment onViewCreated(View view, @Nullable Bundle savedInstanceState):

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    FragmentManager fm = getChildFragmentManager();
    SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentByTag("mapFragment");
    if (mapFragment == null) {
        mapFragment = new SupportMapFragment();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.mapFragmentContainer, mapFragment, "mapFragment");
        ft.commit();
        fm.executePendingTransactions();
    }
    mapFragment.getMapAsync(callback);
}
253

J'ai eu le même problème et j'ai pu le résoudre en supprimant manuellement MapFragment dans la méthode onDestroy() de la classe Fragment. Voici le code qui fonctionne et fait référence à la variable MapFragment par ID dans le code XML:

@Override
public void onDestroyView() {
    super.onDestroyView();
    MapFragment f = (MapFragment) getFragmentManager()
                                         .findFragmentById(R.id.map);
    if (f != null) 
        getFragmentManager().beginTransaction().remove(f).commit();
}

Si vous ne supprimez pas la variable MapFragment manuellement, le processus restera en place de manière à réduire le coût des ressources nécessaires pour recréer/afficher à nouveau la vue cartographique. Il semble que conserver la variable MapView sous-jacente soit très utile pour passer d'un onglet à l'autre, mais lorsqu'il est utilisé dans des fragments, ce comportement entraîne la création d'une copie MapView à chaque nouvelle MapFragment avec le même ID. La solution consiste à supprimer manuellement la MapFragment et à recréer ainsi la carte sous-jacente chaque fois que le fragment est gonflé.

Je l'ai également noté dans une autre réponse [ 1 ].

167
Matt

Voici ma réponse:

1, créer une mise en page XML comme suit:

<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:id="@+id/map_container"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
</FrameLayout>

2, dans la classe Fragment, ajoutez une carte Google par programme.

import com.google.Android.gms.maps.GoogleMap;
import com.google.Android.gms.maps.SupportMapFragment;
import Android.app.Activity;
import Android.os.Bundle;
import Android.support.v4.app.Fragment;
import Android.support.v4.app.FragmentTransaction;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;

/**
 * A simple {@link Android.support.v4.app.Fragment} subclass. Activities that
 * contain this fragment must implement the
 * {@link MapFragment.OnFragmentInteractionListener} interface to handle
 * interaction events. Use the {@link MapFragment#newInstance} factory method to
 * create an instance of this fragment.
 * 
 */
public class MapFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    private GoogleMap mMap;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_map, container, false);
        SupportMapFragment mMapFragment = SupportMapFragment.newInstance();
        mMap = mMapFragment.getMap();
        FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
        transaction.add(R.id.map_container, mMapFragment).commit();
        return view;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        Log.d("Attach", "on attach");
    }

    @Override
    public void onDetach() {
        super.onDetach();
    }
} 
21
Zou
  1. Comme mentionné par @Justin Breitfeller, la solution @Vidar Wahlberg est un hack qui pourrait ne pas fonctionner dans les futures versions d'Android.
  2. @Vidar Wahlberg perfer un hack, car une autre solution pourrait "provoquer la carte recréée et redessinée, ce qui n'est pas toujours souhaitable". La réactualisation de la carte pourrait être empêchée en conservant l'ancien fragment de carte, au lieu de créer une nouvelle instance à chaque fois.
  3. La solution @Matt ne fonctionne pas pour moi (IllegalStateException)
  4. Comme le dit @Justin Breitfeller, "Vous ne pouvez pas gonfler une mise en page dans un fragment lorsque cette mise en page comprend un. Les fragments imbriqués ne sont pris en charge que lorsqu'ils sont ajoutés à un fragment de manière dynamique."

Ma solution:

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

    // init
    //mapFragment = (SupportMapFragment)getChildFragmentManager().findFragmentById(R.id.map);
    // don't recreate fragment everytime ensure last map location/state are maintain
    if (mapFragment == null) {
        mapFragment = SupportMapFragment.newInstance();
        mapFragment.getMapAsync(this);
    }
    FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
    // R.id.map is a layout
    transaction.replace(R.id.map, mapFragment).commit();

    return view;
}
10
Desmond Lua

Déclarer un objet SupportMapFragment globalement

    private SupportMapFragment mapFragment;

Dans la méthode onCreateView (), mettez ci-dessous le code

mapFragment = (SupportMapFragment) getChildFragmentManager()
            .findFragmentById(R.id.map);
 mapFragment.getMapAsync(this);

Dans onDestroyView (), mettez ci-dessous le code

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

    if (mapFragment != null)
        getFragmentManager().beginTransaction().remove(mapFragment).commit();
}

Dans votre fichier XML, mettez le code ci-dessous

 <fragment
    Android:id="@+id/map"
    Android:name="com.abc.Driver.fragment.FragmentHome"
    class="com.google.Android.gms.maps.SupportMapFragment"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    />

Le code ci-dessus a résolu mon problème et il fonctionne bien

4
Digvijay Machale

Je recommanderais replace() plutôt que attach()/detach() dans votre traitement des onglets.

Ou passez à ViewPager. Voici un exemple de projet montrant une ViewPager, avec des onglets, hébergeant 10 cartes. 

3
CommonsWare

Une autre solution:

if (view == null) {
    view = inflater.inflate(R.layout.nearbyplaces, container, false);
}

Ça y est, sinon, vous n'avez pas besoin de le réinitialiser, ce qui est inutile.

2
AnonymousDev

Aujourd'hui, j'ai perdu des heures pour trouver la raison. Heureusement, ce problème ne vient pas de l'implémentation de MapFragment. Malheureusement, cela ne fonctionne pas, car les fragments imbriqués ne sont pris en charge que par le biais de la bibliothèque de support de la version 11.

Mon implémentation a une activité avec une barre d’action (en mode onglet) avec deux onglets (sans viewpager), l’un contenant la carte et l’autre une liste d’entrées. Bien sûr, j'ai été assez naïf pour utiliser MapFragment à l'intérieur de mes fragments d'onglets, et voila, l'application se bloquait à chaque fois que je revenais à map-tab.

(Le même problème que j'aurais aussi au cas où mon fragment de tabulation gonflerait une mise en page contenant un autre fragment).

Une option consiste à utiliser MapView (au lieu de MapFragment), avec quelques frais supplémentaires (voir Documents MapView comme remplacement immédiat dans le fichier layout.xml, une autre option consiste à utiliser support-library à partir de la version précédente. 11 mais prenez ensuite une approche programmatique car les fragments imbriqués ne sont ni pris en charge via la mise en page, ni simplement contourner le problème par programmation en détruisant explicitement le fragment (comme dans la réponse de Matt/Vidar), au fait: le même effet est obtenu à l'aide de MapView (option 1). .

Mais en réalité, je ne voulais pas perdre la carte à chaque fois que je me déplaçais, c’est-à-dire que je voulais la garder en mémoire et le nettoyer uniquement à la fin de mes activités. J’ai donc décidé de simplement masquer/afficher la carte lorsque vous faites une tabulation, voir FragmentTransaction/hide

2
comeGetSome

Je respecte toutes les réponses, mais j’ai trouvé la solution suivante: Si n est le nombre d’onglets, alors:

 mViewPager.setOffscreenPageLimit(n);

Exemple: Dans le cas mentionné:

 mViewPager.setOffscreenPageLimit(2);

View pager implémente une file d'attente, vous ne devez donc pas le laisser supprimer ce fragment. onCreateView n'est appelé qu'une fois.

2
Jayant Arora

Pour ceux qui rencontrent encore ce problème, le meilleur moyen de vous assurer que cette erreur ne se produit pas avec une carte dans un onglet est de faire en sorte que le fragment étende SupportMapFragment au lieu d'imbriquer une SupportMapFragment dans le fragment utilisé pour l'onglet.

Je viens de travailler avec une ViewPager avec une FragmentPagerAdapter, avec le SupportMapFragment dans le troisième onglet.

Voici la structure générale, notez qu'il n'est pas nécessaire de remplacer la méthode onCreateView() et qu'il n'est pas nécessaire de gonfler un layout xml:

public class MapTabFragment extends SupportMapFragment 
                                    implements OnMapReadyCallback {

    private GoogleMap mMap;
    private Marker marker;


    public MapTabFragment() {
    }

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

        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mMap == null) {

            getMapAsync(this);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        mMap = googleMap;
        setUpMap();
    }

    private void setUpMap() {

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


        mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {

            @Override
            public void onMapClick(LatLng point) {

                //remove previously placed Marker
                if (marker != null) {
                    marker.remove();
                }

                //place marker where user just clicked
                marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker")
                        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_Magenta)));

            }
        });

    }


}

Résultat:

enter image description here

Voici le code de classe complet avec lequel j'ai testé, qui comprend le fragment de substitution utilisé pour les deux premiers onglets et le fragment de carte utilisé pour le troisième onglet:

public class MainActivity extends AppCompatActivity implements ActionBar.TabListener{


    SectionsPagerAdapter mSectionsPagerAdapter;

    ViewPager mViewPager;

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

        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        final ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position);
            }
        });

        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
            actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this));
        }

    }


    @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);
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }


    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {

            switch (position) {
                case 0:
                    return PlaceholderFragment.newInstance(position + 1);
                case 1:
                    return PlaceholderFragment.newInstance(position + 1);
                case 2:
                    return MapTabFragment.newInstance(position + 1);
            }

            return null;
        }

        @Override
        public int getCount() {
            // Show 3 total pages.
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            Locale l = Locale.getDefault();

            switch (position) {
                case 0:
                    return getString(R.string.title_section1).toUpperCase(l);
                case 1:
                    return getString(R.string.title_section2).toUpperCase(l);
                case 2:
                    return getString(R.string.title_section3).toUpperCase(l);
            }
            return null;
        }
    }


    public static class PlaceholderFragment extends Fragment {

        private static final String ARG_SECTION_NUMBER = "section_number";

        TextView text;

        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public PlaceholderFragment() {
        }

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

            text = (TextView) rootView.findViewById(R.id.section_label);
            text.setText("placeholder");

            return rootView;
        }
    }

    public static class MapTabFragment extends SupportMapFragment implements
            OnMapReadyCallback {

        private static final String ARG_SECTION_NUMBER = "section_number";

        private GoogleMap mMap;
        private Marker marker;


        public static MapTabFragment newInstance(int sectionNumber) {
            MapTabFragment fragment = new MapTabFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public MapTabFragment() {
        }

        @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.setOnMapClickListener(new GoogleMap.OnMapClickListener() {

                @Override
                public void onMapClick(LatLng point) {

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

                    //remove previously placed Marker
                    if (marker != null) {
                        marker.remove();
                    }

                    //place marker where user just clicked
                    marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker")
                            .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_Magenta)));

                    Log.d("MyMap", "MapClick After Add Marker");

                }
            });

        }
    }
}
2
Daniel Nugent

Je pense qu'il y avait quelques bugs dans la précédente librairie App-Compat pour Child Fragment. J'ai essayé @Vidar Wahlberg et @ Matt's et ils ne travaillaient pas pour moi. Après la mise à jour de la bibliothèque appcompat, mon code fonctionne parfaitement sans effort supplémentaire.

0
maddy d

Dans cette solution, il n'est pas nécessaire de prendre une variable statique.

Button nextBtn;

private SupportMapFragment mMapFragment;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    if (mRootView != null) {
        ViewGroup parent = (ViewGroup) mRootView.getParent();
        Utility.log(0,"removeView","mRootView not NULL");
        if (parent != null) {
            Utility.log(0, "removeView", "view removeViewed");
            parent.removeAllViews();
        }
    }
    else {
        try {
            mRootView = inflater.inflate(R.layout.dummy_fragment_layout_one, container, false);//
        } catch (InflateException e) {
    /* map is already there, just return view as it is  */
            e.printStackTrace();
        }
    }

    return  mRootView;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    FragmentManager fm = getChildFragmentManager();
    SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.mapView);
    if (mapFragment == null) {
        mapFragment = new SupportMapFragment();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.mapView, mapFragment, "mapFragment");
        ft.commit();
        fm.executePendingTransactions();
    }
    //mapFragment.getMapAsync(this);
    nextBtn = (Button) view.findViewById(R.id.nextBtn);
    nextBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Utility.replaceSupportFragment(getActivity(),R.id.dummyFragment,dummyFragment_2.class.getSimpleName(),null,new dummyFragment_2());
        }
    });

}`
0
Lalit kumar

Essayez de définir un identifiant (Android: id = "@ + id/maps_dialog") pour votre disposition parent mapView. Travaille pour moi.

0
hieudev develo

J'avais ceci dans viewPager et le crash était dû au fait que chaque fragment devait avoir sa propre balise, les balises en double ou les identifiants pour le même fragment ne sont pas autorisés.

0
user1396018

Ce qu'il faut noter ici, c'est que votre application va planter dans deux cas: -

1) Afin de réutiliser fragment avec Maps à nouveau, MapView Fragment doit être supprimé lorsque votre fragment montrant que Maps a été remplacé par un autre fragment dans le rappel onDestroyView.

sinon, lorsque vous tentez de gonfler deux fois le même fragment ID double, balise null ou parent avec un autre fragment pour com.google.Android.gms.maps.MapFragment une erreur se produira. 

2) Deuxièmement, vous ne devez pas mélanger les opérations app.Fragment avec Android.support.v4.app.Fragment Les opérations de l'API, par exemple, ne doivent pas utiliser Android.app.FragmentTransaction pour supprimer le type v4.app.Fragment Fragment MapView. Mélanger cela produira à nouveau un crash du côté du fragment. 

Voici un exemple d'extrait de code pour une utilisation correcte de MapView

import Android.content.Context;
import Android.location.Location;
import Android.location.LocationListener;
import Android.location.LocationManager;
import Android.os.Bundle;
import Android.support.v4.app.Fragment;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.Toast;

import com.google.Android.gms.maps.CameraUpdateFactory;
import com.google.Android.gms.maps.GoogleMap;
import com.google.Android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.Android.gms.maps.MapFragment;
import com.google.Android.gms.maps.model.BitmapDescriptorFactory;
import com.google.Android.gms.maps.model.CameraPosition;
import com.google.Android.gms.maps.model.LatLng;
import com.google.Android.gms.maps.model.MarkerOptions;
import com.serveroverload.yago.R;

/**
 * @author 663918
 *
 */
public class HomeFragment extends Fragment implements LocationListener {
    // Class to do operations on the Map
    GoogleMap googleMap;
    private LocationManager locationManager;

    public static Fragment newInstance() {
        return new HomeFragment();
    }

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

        // setuping locatiomanager to perfrom location related operations
        locationManager = (LocationManager) getActivity().getSystemService(
                Context.LOCATION_SERVICE);

        // Requesting locationmanager for location updates
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER, 1, 1, this);

        // To get map from MapFragment from layout
        googleMap = ((MapFragment) getActivity().getFragmentManager()
                .findFragmentById(R.id.map)).getMap();

        // To change the map type to Satellite
        // googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);

        // To show our current location in the map with dot
        // googleMap.setMyLocationEnabled(true);

        // To listen action whenever we click on the map
        googleMap.setOnMapClickListener(new OnMapClickListener() {

            @Override
            public void onMapClick(LatLng latLng) {
                /*
                 * LatLng:Class will give us selected position lattigude and
                 * longitude values
                 */
                Toast.makeText(getActivity(), latLng.toString(),
                        Toast.LENGTH_LONG).show();
            }
        });

        changeMapMode(2);

        // googleMap.setSatellite(true);
        googleMap.setTrafficEnabled(true);
        googleMap.setBuildingsEnabled(true);
        googleMap.setMyLocationEnabled(true);

        return v;
    }

    private void doZoom() {
        if (googleMap != null) {
            googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                    new LatLng(18.520430, 73.856744), 17));
        }
    }

    private void changeMapMode(int mapMode) {

        if (googleMap != null) {
            switch (mapMode) {
            case 0:
                googleMap.setMapType(GoogleMap.MAP_TYPE_NONE);
                break;

            case 1:
                googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                break;

            case 2:
                googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
                break;

            case 3:
                googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
                break;

            case 4:
                googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
                break;

            default:
                break;
            }
        }
    }

    private void createMarker(double latitude, double longitude) {
        // double latitude = 17.385044;
        // double longitude = 78.486671;

        // lets place some 10 random markers
        for (int i = 0; i < 10; i++) {
            // random latitude and logitude
            double[] randomLocation = createRandLocation(latitude, longitude);

            // Adding a marker
            MarkerOptions marker = new MarkerOptions().position(
                    new LatLng(randomLocation[0], randomLocation[1])).title(
                    "Hello Maps " + i);

            Log.e("Random", "> " + randomLocation[0] + ", " + randomLocation[1]);

            // changing marker color
            if (i == 0)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_Azure));
            if (i == 1)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
            if (i == 2)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_CYAN));
            if (i == 3)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
            if (i == 4)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_Magenta));
            if (i == 5)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_ORANGE));
            if (i == 6)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_RED));
            if (i == 7)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_ROSE));
            if (i == 8)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_Violet));
            if (i == 9)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_YELLOW));

            googleMap.addMarker(marker);

            // Move the camera to last position with a zoom level
            if (i == 9) {
                CameraPosition cameraPosition = new CameraPosition.Builder()
                        .target(new LatLng(randomLocation[0], randomLocation[1]))
                        .zoom(15).build();

                googleMap.animateCamera(CameraUpdateFactory
                        .newCameraPosition(cameraPosition));
            }
        }

    }

    /*
     * creating random postion around a location for testing purpose only
     */
    private double[] createRandLocation(double latitude, double longitude) {

        return new double[] { latitude + ((Math.random() - 0.5) / 500),
                longitude + ((Math.random() - 0.5) / 500),
                150 + ((Math.random() - 0.5) * 10) };
    }

    @Override
    public void onLocationChanged(Location location) {

        if (null != googleMap) {
            // To get lattitude value from location object
            double latti = location.getLatitude();
            // To get longitude value from location object
            double longi = location.getLongitude();

            // To hold lattitude and longitude values
            LatLng position = new LatLng(latti, longi);

            createMarker(latti, longi);

            // Creating object to pass our current location to the map
            MarkerOptions markerOptions = new MarkerOptions();
            // To store current location in the markeroptions object
            markerOptions.position(position);

            // Zooming to our current location with zoom level 17.0f
            googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(position,
                    17f));

            // adding markeroptions class object to the map to show our current
            // location in the map with help of default marker
            googleMap.addMarker(markerOptions);
        }

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onProviderEnabled(String provider) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onProviderDisabled(String provider) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onDestroyView() {
        // TODO Auto-generated method stub
        super.onDestroyView();

        locationManager.removeUpdates(this);

        Android.app.Fragment fragment = getActivity().getFragmentManager()
                .findFragmentById(R.id.map);
        if (null != fragment) {
            Android.app.FragmentTransaction ft = getActivity()
                    .getFragmentManager().beginTransaction();
            ft.remove(fragment);
            ft.commit();
        }
    }

}

XML

 <fragment
        Android:id="@+id/map"
        Android:name="com.google.Android.gms.maps.MapFragment"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
       />

Le résultat ressemble à ceci: -  enter image description here

J'espère que ça va aider quelqu'un.

0
Hitesh Sahu

Les fragments imbriqués ne sont actuellement pas pris en charge . Try Support Package, révision 11 .

0
Ivan

Avez-vous essayé de référencer votre classe MapFragment personnalisée dans le fichier de présentation?

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

    <fragment
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:id="@+id/mapFragment"
        Android:name="com.nfc.demo.MapFragment"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />
</LinearLayout>
0
JJD

Si vous n'utilisez que la réponse Vidar Wahlberg, vous obtenez une erreur lorsque vous ouvrez une autre activité (par exemple) et que vous revenez à la carte. Ou dans mon cas, ouvrez une autre activité puis, à partir d'une nouvelle activité, ouvrez de nouveau la carte (sans utiliser le bouton retour) . Mais lorsque vous combinez la solution Vidar Wahlberg et la solution Matt, vous n'aurez aucune exception.

disposition

<com.example.ui.layout.MapWrapperLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
Android:id="@+id/map_relative_layout">

    <RelativeLayout
        Android:layout_width="fill_parent"
        Android:layout_height="fill_parent"
        Android:id="@+id/root">

        <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.SupportMapFragment" />
    </RelativeLayout>
</<com.example.ui.layout.MapWrapperLayout>

Fragment

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    setHasOptionsMenu(true);
    if (view != null) {
        ViewGroup parent = (ViewGroup) view.getParent();
        if (parent != null){
            parent.removeView(view);
        }
    }
    try {
        view = inflater.inflate(R.layout.map_view, null);
        if(view!=null){
            ViewGroup root = (ViewGroup) view.findViewById(R.id.root);
...

@Override
public void onDestroyView() {
    super.onDestroyView();
    Fragment fragment = this.getSherlockActivity().getSupportFragmentManager().findFragmentById(R.id.map);
    if (fragment != null)
        getFragmentManager().beginTransaction().remove(fragment).commit();
}
0
Vlad Hudnitsky