344 votes

Dupliquer, tag null ou le code parent avec un autre fragment de com.google.android.gms.maps.MapFragment

J'ai une application avec trois onglets.

Chaque onglet dispose de sa propre mise en page .fichier xml. L'main.xml a son propre fragment de carte. C'est celui qui s'affiche lorsque l'application se lance.

Tout fonctionne bien sauf quand je change d'onglet. Si j'essaie de revenir à la carte fragment onglet, j'ai cette erreur. De commutation et entre autres onglets fonctionne très bien.

Ce qui pourrait être mal ici?

C'est ma classe principale et mon main.xml ainsi comme une classe 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>

catégorie ( 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

400voto

Vidar Wahlberg Points 1953

La réponse de Matt suggère travaille, mais elle provoque la carte pour être recréé et redessiné, ce qui n'est pas toujours souhaitable. Après beaucoup d'essais et d'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.carte) 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 cette aide, mais je ne peux pas garantir que cela n'a pas d'effets indésirables.

Edit: Il y avait des effets indésirables, tels que lorsque vous quittez l'application et de commencer à nouveau. Depuis l'application n'est pas nécessairement complètement à l'arrêt (mais il suffit de mettre à dormir dans le fond), le code précédent, j'ai présenté serait un échec lors du redémarrage de l'application. J'ai mis à jour le code pour quelque chose qui fonctionne pour moi, tous les deux dans et hors de la carte et de quitter et redémarrer l'application, je ne suis pas trop heureux avec le try-catch peu, mais il semble fonctionner assez bien. Lorsque l'on regarde la trace de la pile, il m'est apparu que je pouvais juste vérifier si le fragment de carte est dans le FragmentManager, pas besoin pour le bloc try-catch, code mis à jour.

Plus des modifications: il s'avère qu'il faut que try-catch, après tout. Juste vérifier pour le fragment de carte s'est avéré ne pas fonctionner si bien, après tout. Blergh.

286voto

Justin Breitfeller Points 6375

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

Remarque: Vous ne pouvez pas gonfler une mise en page dans un fragment lors de la mise en comprend un <fragment>. Imbriqués les fragments ne sont pris en charge lorsqu'il est ajouté un fragment de façon dynamique.

Alors que vous pouvez être en mesure d'accomplir la tâche avec les hacks présenté ici, je vous suggère fortement de ne pas le faire. Il est impossible d'être sûr que ces hacks va gérer ce que chaque nouveau système d'exploitation Android est le cas lorsque vous essayez de gonfler une mise en page d'un fragment contenant un autre fragment.

Le seul pris en charge Android pour ajouter un fragment à un autre fragment est par l'intermédiaire d'une transaction à partir de l'enfant fragment de manager.

186voto

Matt Points 1410

J'ai eu le même problème et a pu le résoudre en supprimant manuellement l' MapFragment dans la onDestroy() méthode de Fragment classe. Voici le code qui fonctionne et les références de l' MapFragment par ID dans le fichier 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 retirez pas l' MapFragment manuellement, il va s'accrocher autour de sorte qu'il ne coûte pas beaucoup de ressources pour recréer/afficher la carte afficher de nouveau. Il semble que le maintien du sous-jacent MapView est idéal pour des allers-retours entre les onglets, mais lorsqu'il est utilisé dans des fragments de ce comportement provoque un duplicata MapView à être créé à chaque nouvelle MapFragment avec le même ID. La solution est de supprimer manuellement l' MapFragment et ainsi recréer la carte sous-jacente à chaque fois que le fragment est gonflé.

J'ai aussi noté cela dans une autre réponse [1].

23voto

Zou Points 101

Voici ma réponse :

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

2, dans la classe de Fragment, ajouter une carte google map par programmation.

3voto

CommonsWare Points 402670

Je recommanderais replace() plutôt que attach() / detach() dans votre gestion d'onglet.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X