Ich habe eine Anwendung mit drei Registerkarten.
Jede Registerkarte verfügt über eine eigene XML-Layoutdatei. Die main.xml hat ein eigenes Kartenfragment. Es wird angezeigt, 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, wird dieser Fehler angezeigt. Das Wechseln zu und zwischen anderen Registerkarten funktioniert einwandfrei.
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 auch 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
Antworten:
Die Antwort, die Matt vorschlägt, funktioniert, aber die Karte wird neu erstellt und neu gezeichnet, was nicht immer wünschenswert ist. Nach vielen Versuchen und Irrtümern fand ich eine Lösung, die für mich funktioniert:
Hier ist "map.xml" (R.layout.map) mit R.id.mapFragment (android: id = "@ + id / mapFragment"):
Ich hoffe, das hilft, aber ich kann nicht garantieren, dass es keine nachteiligen Auswirkungen hat.
Bearbeiten: Es gab einige nachteilige Auswirkungen, z. B. beim Beenden und erneuten Starten der Anwendung. Da die Anwendung nicht unbedingt vollständig heruntergefahren wird (sondern nur im Hintergrund in den Ruhezustand versetzt wird), schlägt der zuvor eingereichte Code beim Neustart der Anwendung fehl. Ich habe den Code auf etwas aktualisiert, das für mich funktioniert, sowohl beim Ein- und Aussteigen aus der Karte als auch beim Beenden und Neustarten der Anwendung. Ich bin mit dem Try-Catch-Bit nicht allzu zufrieden, aber es scheint gut genug zu funktionieren.
Beim Betrachten der Stapelverfolgung kam mir der Gedanke, dass ich nur überprüfen konnte, ob sich das Kartenfragment im FragmentManager befindet, ohne dass der Try-Catch-Block benötigt wurde und der Code aktualisiert wurde.Weitere Änderungen: Es stellt sich heraus, dass Sie diesen Try-Catch doch brauchen. Nur nach dem Kartenfragment zu suchen, stellte sich schließlich als nicht so gut heraus. Blergh.
quelle
SupportMapFragment
in-onDestroyView
Methode manuell zu entfernen .Das Problem ist, dass das, was Sie versuchen, nicht getan werden sollte. Sie sollten keine Fragmente in anderen Fragmenten aufblasen. Aus der Android- Dokumentation :
Obwohl Sie die Aufgabe möglicherweise mit den hier vorgestellten Hacks ausführen können, empfehle ich Ihnen dringend, dies nicht zu tun. Es ist unmöglich sicher zu sein, dass diese Hacks die Aufgaben jedes neuen Android-Betriebssystems erfüllen, wenn Sie versuchen, ein Layout für ein Fragment mit einem anderen Fragment aufzublasen.
Die einzige von Android unterstützte Möglichkeit, ein Fragment einem anderen Fragment hinzuzufügen, besteht in einer Transaktion des untergeordneten Fragmentmanagers.
Ändern Sie einfach Ihr XML-Layout in einen leeren Container (fügen Sie bei Bedarf eine ID hinzu):
Dann in der Fragment-
onViewCreated(View view, @Nullable Bundle savedInstanceState)
Methode:quelle
4.3
als ich einSupportMapFragment
in XML definiertes verwendet habe. Das Problem wurde behoben, indem das Fragment dynamisch erstellt und in eine Containeransicht eingefügt wurde. Siehe diese SO-Antwort .SupportMapFragment.newInstance();
developer.google.com/maps/documentation/android-api/mapIch hatte das gleiche Problem und konnte es beheben, indem ich das
MapFragment
in deronDestroy()
Methode derFragment
Klasse manuell entfernte . Hier ist Code, der funktioniert und auf dieMapFragment
By-ID im XML verweist :Wenn Sie das nicht
MapFragment
manuell entfernen , bleibt es hängen, sodass das erneute Erstellen / Anzeigen der Kartenansicht nicht viele Ressourcen kostet. Es scheint, dass das Beibehalten des BasiswertsMapView
für das Hin- und Herwechseln zwischen Registerkarten ideal ist. Bei Verwendung in Fragmenten führt dieses Verhalten jedoch dazuMapView
, dass bei jedem neuenMapFragment
mit derselben ID ein Duplikat erstellt wird . Die Lösung besteht darin, das manuell zu entfernenMapFragment
, die zugrunde liegende Karte bei jedem Aufblasen des Fragments und somit neu zu erstellen.Ich habe dies auch in einer anderen Antwort vermerkt [ 1 ].
quelle
Das ist meine Antwort:
1, Erstellen Sie eine Layout-XML wie folgt:
2 Fügen Sie in der Fragment-Klasse programmgesteuert eine Google Map hinzu.
quelle
mMapFragment.getMap();
kehrt zurücknull
. Irgendeine Idee warum?Meine Lösung:
quelle
Deklarieren Sie das SupportMapFragment-Objekt global
In der onCreateView () -Methode wird der folgende Code eingefügt
Geben Sie in onDestroyView () den folgenden Code ein
Geben Sie in Ihrer XML-Datei den folgenden Code ein
Der obige Code hat mein Problem gelöst und funktioniert einwandfrei
quelle
Ich würde
replace()
eher empfehlen alsattach()
/detach()
in Ihrer Tab-Behandlung.Oder wechseln Sie zu
ViewPager
. Hier ist ein Beispielprojekt, dasViewPager
mit Registerkarten 10 Karten hostet.quelle
Eine andere Lösung:
Das ist es, wenn nicht null, müssen Sie es nicht neu initialisieren. Das Entfernen vom übergeordneten Element ist ein unnötiger Schritt.
quelle
Ich habe heute Stunden verloren, um den Grund zu finden. Glücklicherweise liegt dieses Problem nicht an der Implementierung von MapFragment. Leider funktioniert dies nicht, da verschachtelte Fragmente nur über die Support-Bibliothek ab Version 11 unterstützt werden.
Meine Implementierung hat eine Aktivität mit Aktionsleiste (im Registerkartenmodus) mit zwei Registerkarten (kein Viewpager), von denen eine die Karte und die andere eine Liste von Einträgen enthält. Natürlich war ich ziemlich naiv, MapFragment in meinen Tab-Fragmenten zu verwenden, und voila, die App stürzte jedes Mal ab, wenn ich zurück zu Map-Tab wechselte.
(Das gleiche Problem hätte ich auch für den Fall, dass mein Tab-Fragment ein Layout mit einem anderen Fragment aufblasen würde).
Eine Möglichkeit besteht darin, MapView (anstelle von MapFragment) mit etwas Aufwand zu verwenden (siehe MapView- Dokumente) als Drop-In-Ersatz in der layout.xml). Eine andere Möglichkeit besteht darin, die Support-Bibliothek ab Version 11 zu verwenden und dann programmatisch vorzugehen Da verschachtelte Fragmente weder über das Layout unterstützt werden, noch einfach programmgesteuert umgangen werden, indem das Fragment explizit zerstört wird (wie in der Antwort von Matt / Vidar), wird übrigens der gleiche Effekt mit MapView erzielt (Option 1).
Aber eigentlich wollte ich die Karte nicht jedes Mal verlieren, wenn ich sie abblätterte, das heißt, ich wollte sie nur nach Abschluss der Aktivität im Speicher behalten und bereinigen. Deshalb habe ich beschlossen, die Karte während des Tabulierens einfach auszublenden / anzuzeigen, siehe FragmentTransaction / hide
quelle
Für diejenigen, die immer noch auf dieses Problem stoßen, besteht der beste Weg, um sicherzustellen, dass dieser Fehler bei einer Karte in einem Tab nicht auftritt, darin, das Fragment zu erweitern,
SupportMapFragment
anstatt einSupportMapFragment
in das für den Tab verwendetes Fragment zu verschachteln .Ich habe das gerade mit a
ViewPager
mit a funktioniertFragmentPagerAdapter
, mit dem SupportMapFragment auf der dritten Registerkarte.Hier ist die allgemeine Struktur. Beachten Sie, dass die
onCreateView()
Methode nicht überschrieben werden muss und keine Layout-XML-Datei aufgeblasen werden muss:Ergebnis:
Hier ist der vollständige Klassencode, mit dem ich getestet habe, einschließlich des Platzhalterfragments für die ersten beiden Registerkarten und des Kartenfragments für die dritte Registerkarte:
quelle
Ich respektiere alle Antworten, aber ich habe diese Einzeiler-Lösung gefunden: Wenn n die Anzahl der Registerkarten ist, dann:
Beispiel: Im genannten Fall:
View Pager implementiert eine Warteschlange, sodass Sie dieses Fragment nicht entfernen lassen müssen. onCreateView wird nur einmal aufgerufen.
quelle
Sie kehren zweimal zurück oder blasen das Layout auf. Überprüfen Sie nur, ob Sie nur einmal aufblasen.
quelle
Verschachtelte Fragmente werden derzeit nicht unterstützt. Versuchen Sie Support Package, Revision 11 .
quelle
Haben Sie versucht, Ihre benutzerdefinierte
MapFragment
Klasse in der Layoutdatei zu referenzieren ?quelle
Wenn Sie nur die Antwort von Vidar Wahlberg verwenden, wird eine Fehlermeldung angezeigt, wenn Sie eine andere Aktivität öffnen (z. B.) und zur Karte zurückkehren. Oder öffnen Sie in meinem Fall eine andere Aktivität und öffnen Sie dann von einer neuen Aktivität aus die Karte erneut (ohne die Schaltfläche "Zurück" zu verwenden). Wenn Sie jedoch die Vidar Wahlberg-Lösung und die Matt-Lösung kombinieren, gibt es keine Ausnahmen.
Layout
Fragment
quelle
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 erlaubt.
quelle
Ich denke, es gab einige Fehler in der vorherigen App-Compat-Bibliothek für untergeordnetes Fragment. Ich habe @Vidar Wahlberg und @ Matt's ans ausprobiert und sie haben bei mir nicht funktioniert. Nach dem Aktualisieren der Appcompat-Bibliothek läuft mein Code ohne zusätzlichen Aufwand einwandfrei.
quelle
Hier ist zu beachten, dass Ihre App in zwei Fällen schwer abstürzt: -
Hier ist ein Beispielcode-Snippet für die korrekte Verwendung von MapView
XML
Ergebnis sieht so aus: -
Hoffe es wird jemandem helfen.
quelle
In dieser Lösung müssen Sie keine statische Variable verwenden.
quelle
Ich bin etwas spät zur Party, aber keine dieser Antworten hat mir in meinem Fall geholfen. Ich habe Google Map als SupportMapFragment und PlaceAutocompleteFragment in meinem Fragment verwendet. Wie alle Antworten darauf hinwiesen, dass das Problem darin besteht, dass SupportMapFragment die Karte ist, die neu erstellt und neu gezeichnet werden soll. Nachdem ich jedoch herausgefunden hatte, dass mein Problem tatsächlich bei PlaceAutocompleteFragment lag
Hier ist die funktionierende Lösung für diejenigen, die aufgrund von SupportMapFragment und SupportMapFragment mit diesem Problem konfrontiert sind
Und in onDestroyView löschen Sie SupportMapFragment und SupportMapFragment
quelle
Versuchen Sie, eine ID (android: id = "@ + id / maps_dialog") für Ihr übergeordnetes mapView-Layout festzulegen. Funktioniert bei mir.
quelle
Wenn Sie jetzt hierher kommen und diese Art von Fehler beim Öffnen des einen
Dialog
oder anderenFragment
mit Google Places erhaltenAutocompleteSupportFragment
, versuchen Sie diesen Einzeiler (ich weiß nicht, wie sicher dies ist, aber es funktioniert für mich):autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();
bevor Sie Ihr Fragment entlassen / zerstören.
quelle
Warum fügen Sie keine Karte mit dem MapView-Objekt anstelle von MapFragment ein? Ich bin mir nicht sicher, ob es in MapView Einschränkungen gibt, obwohl ich es hilfreich fand.
quelle