web-dev-qa-db-de.com

Doppelte ID, Tag null oder übergeordnete ID mit einem anderen Fragment für com.google.Android.gms.maps.MapFragment

Ich habe eine Anwendung mit drei Registerkarten.

Jede Registerkarte hat eine eigene XML-Layoutdatei. Die main.xml hat ein eigenes Kartenfragment. Es ist der, der angezeigt wird, wenn die Anwendung zum ersten Mal gestartet wird.

Alles funktioniert gut, außer wenn ich zwischen Registerkarten wechsle. Wenn ich versuche, zur Registerkarte "Kartenfragment" zurückzukehren, erhalte ich diese Fehlermeldung. Das Wechseln zu und zwischen anderen Registerkarten funktioniert problemlos.

Was könnte hier falsch sein?

Dies ist meine Hauptklasse und meine main.xml sowie eine relevante Klasse, die ich verwende (das Fehlerprotokoll finden Sie unten).

Hauptklasse

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>

relevante Klasse (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();
    }
}

Error  

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

Die Antwort, die Matt vorschlägt, schlägt vor, funktioniert jedoch neu, und die Karte wird neu erstellt und neu gezeichnet, was nicht immer wünschenswert ist .. Nach vielen Versuchen und Fehlern habe ich eine Lösung gefunden, die für mich funktioniert:

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

Hier ist "map.xml" (R.layout.map) mit 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>

Ich hoffe, das hilft, aber ich kann nicht garantieren, dass es keine negativen Auswirkungen hat.

Bearbeiten: Es gab einige nachteilige Auswirkungen, z. B. wenn Sie die Anwendung beenden und erneut starten. Da die Anwendung nicht notwendigerweise vollständig heruntergefahren wird (sondern nur im Hintergrund in den Ruhezustand versetzt wird), schlägt der zuvor gesendete Code nach dem Neustart der Anwendung fehl. Ich habe den Code so aktualisiert, dass er für mich funktioniert, sowohl in die Karte hinein und wieder herausgefahren als auch die Anwendung beendet und neu gestartet wird. Ich bin mit dem Try-Catch-Bit nicht allzu glücklich, aber es scheint gut genug zu funktionieren. Bei der Betrachtung der Stack-Trace kam mir der Gedanke, dass ich einfach überprüfen könnte, ob das Kartenfragment im FragmentManager enthalten ist. Der try-catch-Block ist nicht erforderlich, der Code wurde aktualisiert.

Weitere Bearbeitungen: Es stellt sich heraus, dass Sie den Try-Catch doch brauchen. Die Suche nach dem Kartenfragment erwies sich als nicht so gut. Blergh.

398
Vidar Wahlberg

Das Problem ist, dass das, was Sie versuchen, nicht getan werden sollte. Sie sollten keine Fragmente in anderen Fragmenten aufblasen. Aus Android Dokumentation :

Hinweis: Sie können ein Layout nicht in ein Fragment aufpumpen, wenn dieses Layout enthält ein <fragment>. Verschachtelte Fragmente werden nur unterstützt, wenn sie hinzugefügt werden dynamisch zu einem Fragment.

Sie können zwar die Aufgabe mit den hier vorgestellten Hacks erledigen, aber ich empfehle Ihnen dringend, dies nicht zu tun. Es ist unmöglich, sicher zu sein, dass diese Hacks das Verhalten jedes neuen Android-Betriebssystems beherrschen, wenn Sie versuchen, ein Layout für ein Fragment mit einem anderen Fragment aufzublähen. 

Die einzige von Android unterstützte Methode zum Hinzufügen eines Fragments zu einem anderen Fragment besteht in einer Transaktion des untergeordneten Fragment-Managers.

Ändern Sie einfach Ihr XML-Layout in einen leeren Container (fügen Sie ggf. eine ID hinzu): 

<?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>

Dann in der Fragment onViewCreated(View view, @Nullable Bundle savedInstanceState) Methode:

@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

Ich hatte das gleiche Problem und konnte dieses Problem durch manuelles Entfernen der MapFragment in der onDestroy()-Methode der Fragment-Klasse beheben. Hier ist der Code, der funktioniert und auf die MapFragment anhand der ID im XML verweist:

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

Wenn Sie die MapFragment nicht manuell entfernen, bleibt sie hängen, so dass es nicht viel Ressourcen kostet, die Kartenansicht erneut zu erstellen/anzuzeigen. Das Beibehalten der zugrundeliegenden MapView scheint großartig für das Wechseln zwischen Registerkarten zu sein. Bei Verwendung in Fragmenten bewirkt dieses Verhalten jedoch, dass bei jeder neuen MapView mit derselben ID ein Duplikat MapFragment erstellt wird. Die Lösung besteht darin, die Variable MapFragment manuell zu entfernen und somit die zugrunde liegende Karte jedes Mal neu zu erstellen, wenn das Fragment aufgeblasen wird.

Ich habe dies auch in einer anderen Antwort [ 1 ] notiert.

167
Matt

Das ist meine Antwort:

1. Erstellen Sie eine Layout-XML wie folgt:

<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 Fügen Sie in der Klasse Fragment eine Google-Map programmgesteuert hinzu.

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. Wie von @Justin Breitfeller erwähnt, ist die @Vidar Wahlberg-Lösung ein Hack, der in zukünftigen Versionen von Android möglicherweise nicht funktioniert.
  2. @Vidar Wahlberg hat einen Hack, weil andere Lösungen "die Karte neu erstellen und neu zeichnen lassen, was nicht immer wünschenswert ist". Das erneute Zeichnen von Karten könnte verhindert werden, indem das alte Kartenfragment beibehalten wird, anstatt jedes Mal eine neue Instanz zu erstellen.
  3. @Matt-Lösung funktioniert für mich nicht (IllegalStateException)
  4. @Justin Breitfeller zitiert: "Sie können ein Layout nicht in ein Fragment aufpumpen, wenn dieses Layout eine enthält. Verschachtelte Fragmente werden nur unterstützt, wenn sie dynamisch zu einem Fragment hinzugefügt werden."

Meine Lösung:

@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

Deklarieren Sie das SupportMapFragment-Objekt global

    private SupportMapFragment mapFragment;

In der onCreateView () -Methode unterhalb des Codes

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

In onDestroyView () unter dem Code einfügen

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

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

Geben Sie in Ihre XML-Datei den folgenden Code ein

 <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"
    />

Der obige Code hat mein Problem gelöst und es funktioniert gut

4

Ich würde replace() anstelle von attach()/detach() in Ihrer Tab-Handhabung empfehlen.

Oder wechseln Sie zu ViewPager. Hier ist ein Beispielprojekt , das eine ViewPager mit Registerkarten zeigt, die 10 Karten enthält. 

3
CommonsWare

Eine andere Lösung:

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

Das ist es, wenn Sie nicht null sind, müssen Sie sie nicht erneut initialisieren.

2
AnonymousDev

Ich habe heute Stunden verloren, um den Grund zu finden. Glücklicherweise liegt dieses Problem nicht an der MapFragment-Implementierung. Leider funktioniert dies nicht, da geschachtelte Fragmente nur durch die Support-Bibliothek ab Version 11 unterstützt werden.

Meine Implementierung verfügt über eine Aktivität mit Aktionsleiste (im Registerkartenmodus) mit zwei Registerkarten (kein Viewpager), von denen eine die Karte und die andere eine Liste mit Einträgen enthält. Natürlich war ich ziemlich naiv, MapFragment in meinen Tab-Fragmenten zu verwenden. Die App stürzte jedes Mal ab, wenn ich wieder auf den Map-Tab wechselte.

(Das gleiche Problem würde ich auch haben, falls mein Tab-Fragment jedes Layout mit einem anderen Fragment aufbläst).

Eine Option ist die Verwendung von MapView (anstelle von MapFragment), jedoch mit einem gewissen Aufwand (siehe MapView Docs als Drop-In-Ersetzung in der Datei layout.xml). Eine andere Option ist die Verwendung der Support-Library ab Version 11, jedoch Nehmen Sie dann einen programmatischen Ansatz an, da verschachtelte Fragmente weder über das Layout unterstützt werden, noch programmatisch umgangen werden, indem Sie das Fragment explizit zerstören (wie in der Antwort von Matt/Vidar).

Aber eigentlich wollte ich die Karte nicht jedes Mal verlieren, wenn ich die Registerkarte entferne. Das heißt, ich wollte sie nur nach Abschluss der Aktivitäten im Speicher behalten und bereinigen. Daher beschloss ich, die Karte einfach während des Tabulierens ein-/auszublenden, siehe FragmentTransaction/verbergen

2
comeGetSome

Ich respektiere alle Antworten, aber ich habe diese einzeilige Lösung gefunden: Wenn n die Anzahl der Registerkarten ist, dann:

 mViewPager.setOffscreenPageLimit(n);

Beispiel: Falls erwähnt:

 mViewPager.setOffscreenPageLimit(2);

View-Pager implementiert eine Warteschlange, sodass Sie dieses Fragment nicht entfernen müssen. onCreateView wird nur einmal aufgerufen.

2
Jayant Arora

Für diejenigen, die immer noch mit diesem Problem konfrontiert sind, ist der beste Weg, um sicherzustellen, dass Sie diesen Fehler nicht mit einer Karte in einem Tab erhalten, das Fragment SupportMapFragment zu erweitern, anstatt eine SupportMapFragment innerhalb des für das Tab verwendeten Fragments zu verschachteln.

Ich habe dies gerade mit einer ViewPager mit einer FragmentPagerAdapter und dem SupportMapFragment im dritten Tab erreicht.

Hier ist die allgemeine Struktur. Beachten Sie, dass Sie die onCreateView()-Methode nicht überschreiben müssen und Layout-XML-Dateien nicht aufgeblasen werden müssen:

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

            }
        });

    }


}

Ergebnis:

enter image description here

Hier ist der vollständige Klassencode, mit dem ich getestet habe, einschließlich des Platzhalter-Fragments, das für die ersten beiden Registerkarten verwendet wird, und des Map-Fragments, das für das dritte Tab verwendet wurde:

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

Ich denke, es gab einige Fehler in der vorherigen App-Compat-Bibliothek für untergeordnetes Fragment. Ich habe @Vidar Wahlberg und @ Matt's ausprobiert, sie arbeiteten nicht für mich. Nach der Aktualisierung der appcompat-Bibliothek läuft mein Code ohne zusätzlichen Aufwand einwandfrei.

0
maddy d

Bei dieser Lösung müssen Sie keine statische Variable verwenden.

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

Legen Sie für Ihr mapView-übergeordnetes Layout eine ID (Android: id = "@ + id/maps_dialog") fest. Funktioniert bei mir.

0
hieudev develo

Ich hatte dies in viewPager und der Absturz war, weil jedes Fragment ein eigenes Tag haben musste, doppelte Tags oder IDs für dasselbe Fragment sind nicht zulässig.

0
user1396018

Ich bin ein bisschen zu spät zur Party, aber keine dieser Antworten hat mir in meinem Fall geholfen. Ich habe Google map als SupportMapFragment und PlaceAutocompleteFragment beide in meinem Fragment verwendet. Da alle Antworten darauf hinwiesen, dass das Problem darin liegt, dass SupportMapFragment die Karte ist, die neu erstellt und neu gezeichnet werden soll. Aber nachdem ich herausgefunden hatte, war mein Problem tatsächlich mit PlaceAutocompleteFragment

Hier ist also die funktionierende Lösung für alle, die dieses Problem aufgrund von SupportMapFragment und SupportMapFragment haben.

 //Global SupportMapFragment mapFragment;
 mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.mapFragment);
    FragmentManager fm = getChildFragmentManager();

    if (mapFragment == null) {
        mapFragment = SupportMapFragment.newInstance();
        fm.beginTransaction().replace(R.id.mapFragment, mapFragment).commit();
        fm.executePendingTransactions();
    }

    mapFragment.getMapAsync(this);

    //Global PlaceAutocompleteFragment autocompleteFragment;


    if (autocompleteFragment == null) {
        autocompleteFragment = (PlaceAutocompleteFragment) getActivity().getFragmentManager().findFragmentById(R.id.place_autoCompleteFragment);

    }

Deaktivieren Sie in onDestroyView das SupportMapFragment und das SupportMapFragment

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


    if (getActivity() != null) {
        Log.e("res","place dlted");
        Android.app.FragmentManager fragmentManager = getActivity().getFragmentManager();
        Android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.remove(autocompleteFragment);
        fragmentTransaction.commit(); 
       //Use commitAllowingStateLoss() if getting exception 

        autocompleteFragment = null;
    }


}
0
Ratul Bin Tazul

Zu beachten Hier ist, dass Ihre App in zwei Fällen stark abstürzt: -

1) Um Fragment erneut mit Maps zu verwenden, muss MapView Fragment bei .__ entfernt werden. Ihr Fragment mit Maps wurde im onDestroyView-Callback durch ein anderes Fragment ersetzt.

andernfalls, wenn Sie versuchen, dasselbe Fragment zweimal aufzublähen Doppelte ID, Tag null oder übergeordnete ID mit einem anderen Fragment für com.google.Android.gms.maps.MapFragment

2) Zweitens dürfen Sie app.Fragment-Vorgänge nicht mit .__ mischen. Android.support.v4.app.Fragment-API-Operationen, z. B..... Nicht verwenden Android.app.FragmentTransaction zum Entfernen von v4.app.Fragment-Typ MapView-Fragment. Das Mischen dieses Ergebnisses führt erneut zu einem Absturz von der Fragmentseite. 

Hier ist ein Beispiel-Codeausschnitt für die korrekte Verwendung von 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"
       />

Ergebnis sieht so aus: -  enter image description here

Ich hoffe es wird jemandem helfen.

0
Hitesh Sahu

Geschachtelte Fragmente werden derzeit nicht unterstützt . Try Support Package, Revision 11 .

0
Ivan

Haben Sie versucht, auf Ihre benutzerdefinierte MapFragment-Klasse in der Layoutdatei zu verweisen?

<?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

Wenn Sie nur die Antwort von Vidar Wahlberg verwenden, erhalten Sie eine Fehlermeldung, wenn Sie eine andere Aktivität (z. B.) öffnen und zur Karte zurückkehren. Oder öffnen Sie in meinem Fall eine andere Aktivität und öffnen Sie dann aus einer neuen Aktivität die Map erneut (ohne Zurück-Schaltfläche). Wenn Sie jedoch die Vidar Wahlberg-Lösung und die Matt-Lösung kombinieren, haben Sie keine Ausnahmen.

layout

<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