Mam aplikację z trzema zakładkami.
Każda karta ma własny plik .xml układu. Plik main.xml ma własny fragment mapy. To ten, który pojawia się przy pierwszym uruchomieniu aplikacji.
Wszystko działa dobrze, z wyjątkiem sytuacji, gdy zmieniam karty. Jeśli spróbuję wrócić do karty fragmentu mapy, pojawia się ten błąd. Przełączanie między kartami i między nimi działa dobrze.
Co może być nie tak?
To jest moja główna klasa i mój main.xml, a także odpowiednia klasa, której używam (dziennik błędów znajduje się również na dole)
główna klasa
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>
odpowiednia klasa (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();
}
}
błąd
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
Odpowiedzi:
Odpowiedź, którą sugeruje Matt, działa, ale powoduje, że mapa jest odtwarzana i przerysowywana, co nie zawsze jest pożądane. Po wielu próbach i błędach znalazłem rozwiązanie, które działa dla mnie:
Dla lepszej oceny, oto „map.xml” (R.layout.map) z R.id.mapFragment (android: id = "@ + id / mapFragment"):
Mam nadzieję, że to pomaga, ale nie mogę zagwarantować, że nie będzie miało to żadnych negatywnych skutków.
Edycja: Wystąpiły pewne negatywne skutki, na przykład podczas zamykania aplikacji i ponownego uruchamiania. Ponieważ aplikacja niekoniecznie musi zostać całkowicie zamknięta (ale po prostu uśpiona w tle), poprzedni kod, który przesłałem, nie powiedzie się po ponownym uruchomieniu aplikacji. Zaktualizowałem kod do czegoś, co działa dla mnie, wchodząc i wychodząc z mapy oraz wychodząc i uruchamiając ponownie aplikację. Nie jestem zbyt zadowolony z bitu try-catch, ale wydaje się, że działa wystarczająco dobrze.
Kiedy patrzyłem na ślad stosu, przyszło mi do głowy, że mogę po prostu sprawdzić, czy fragment mapy znajduje się w FragmentManager, nie potrzeba bloku try-catch, zaktualizowano kod.Więcej zmian: okazuje się, że mimo wszystko potrzebujesz tego catch-catch. Samo sprawdzenie fragmentu mapy okazało się jednak nie działać tak dobrze. Blergh.
źródło
SupportMapFragment
inonDestroyView
.Problem polega na tym, że to, co próbujesz zrobić, nie powinno być zrobione. Nie powinieneś pompować fragmentów wewnątrz innych fragmentów. Z dokumentacji Androida :
Chociaż możesz być w stanie wykonać zadanie za pomocą przedstawionych tutaj hacków, zdecydowanie sugeruję, abyś tego nie robił. Nie można mieć pewności, że te hacki poradzą sobie z tym, co robi każdy nowy system operacyjny Android, gdy próbujesz zawyżać układ dla fragmentu zawierającego inny fragment.
Jedynym obsługiwanym przez Androida sposobem dodania fragmentu do innego fragmentu jest transakcja z menedżera fragmentów potomnych.
Po prostu zmień układ XML na pusty kontener (w razie potrzeby dodaj identyfikator):
Następnie w
onViewCreated(View view, @Nullable Bundle savedInstanceState)
metodzie Fragment :źródło
4.3
podczas korzystania zeSupportMapFragment
zdefiniowanego w XML. Dynamiczne utworzenie fragmentu i wstrzyknięcie go do widoku kontenera rozwiązało problem. Zobacz tę SO odpowiedź .SupportMapFragment.newInstance();
developers.google.com/maps/documentation/android-api/mapMiałem ten sam problem i udało się go rozwiązać poprzez ręczne usunięcie
MapFragment
wonDestroy()
sposobieFragment
klasie. Oto kod, który działa i odwołuje sięMapFragment
do identyfikatora w pliku XML:Jeśli nie usuniesz
MapFragment
ręcznie, zostanie on zawieszony, aby odtworzenie / pokazanie widoku mapy nie kosztowało dużo zasobów. Wygląda na to, że zachowanie bazyMapView
jest świetne do przełączania się między kartami, ale gdy jest używane we fragmentach, takie zachowanie powoduje tworzenie duplikatuMapView
przy każdym nowymMapFragment
o tym samym ID. Rozwiązaniem jest ręczne usunięcie,MapFragment
a tym samym odtworzenie podstawowej mapy za każdym razem, gdy fragment zostanie napompowany.Zauważyłem to również w innej odpowiedzi [ 1 ].
źródło
Oto moja odpowiedź:
1, Utwórz układ xml w następujący sposób:
2, w klasie Fragment dodaj programowo mapę google.
źródło
mMapFragment.getMap();
zwracanull
. Masz pomysł, dlaczego?Moje rozwiązanie:
źródło
Deklaruj obiekt SupportMapFragment globalnie
W metodzie onCreateView () umieść poniższy kod
W onDestroyView () umieść poniżej kodu
W swoim pliku xml umieść poniższy kod
Powyższy kod rozwiązał mój problem i działa dobrze
źródło
Polecam
replace()
raczej niżattach()
/detach()
w obsłudze kart.Lub zmień na
ViewPager
. Oto przykładowy projekt pokazującyViewPager
, z zakładkami, hosting 10 map.źródło
Inne rozwiązanie:
To tyle, jeśli nie zero, nie trzeba go ponownie inicjować, usuwanie z elementu nadrzędnego jest niepotrzebnym krokiem.
źródło
Dziś straciłem godziny na znalezienie przyczyny, na szczęście ten problem nie wynika z implementacji MapFragment, niestety to nie działa, ponieważ zagnieżdżone fragmenty są obsługiwane tylko przez bibliotekę wsparcia z wersji 11.
Moja implementacja ma działanie z paskiem akcji (w trybie kart) z dwiema kartami (bez przeglądarki), jedna z mapą, a druga z listą wpisów. Oczywiście byłem dość naiwny, gdy korzystałem z MapFragment w moich fragmentach kart, i voila aplikacja ulegała awarii za każdym razem, gdy wracałem do mapy.
(Ten sam problem, który miałbym również w przypadku, gdyby mój fragment tabulatora nadmuchał jakikolwiek układ zawierający jakikolwiek inny fragment).
Jedną z opcji jest użycie MapView (zamiast MapFragment), jednak z pewnym narzutem (patrz Dokumenty MapView jako zamiennik drop-in w layout.xml, inną opcją jest użycie biblioteki pomocniczej w górę od wersji 11, ale następnie podejście programowe ponieważ zagnieżdżone fragmenty nie są obsługiwane przez układ lub po prostu programowo działają poprzez jawne zniszczenie fragmentu (jak w odpowiedzi Matt / Vidar), btw: ten sam efekt osiąga się przy użyciu MapView (opcja 1).
Ale tak naprawdę nie chciałem zgubić mapy za każdym razem, gdy odsuwam kartę, to znaczy chciałem zachować ją w pamięci i wyczyścić tylko po zamknięciu aktywności, więc postanowiłem po prostu ukryć / pokazać mapę podczas tabulacji, zobacz FragmentTransakcja / ukryj
źródło
Dla tych, którzy wciąż napotykają ten problem, najlepszym sposobem, aby upewnić się, że nie pojawia się ten błąd z mapą w zakładce, jest wydłużenie fragmentu
SupportMapFragment
zamiast zagnieżdżeniaSupportMapFragment
wewnątrz fragmentu używanego na karcie.Właśnie dostałem to za
ViewPager
pomocą aFragmentPagerAdapter
, z SupportMapFragment w trzeciej zakładce.Oto ogólna struktura, zauważ, że nie ma potrzeby przesłonięcia
onCreateView()
metody i nie ma potrzeby pompowania żadnego xml układu:Wynik:
Oto pełny kod klasy, którego użyłem do testowania, w tym fragment zastępczy Fragment użyty dla pierwszych dwóch Tab i Fragment mapy użyty dla trzeciej Tab:
źródło
Szanuję wszystkie odpowiedzi, ale znalazłem jedno rozwiązanie liniowe: Jeśli n to liczba zakładek, to:
Przykład: w przypadku wspomnianym:
Pager widoku implementuje kolejkę, więc nie musisz pozwalać jej usuwać tego fragmentu. onCreateView jest wywoływany tylko raz.
źródło
Zwracasz lub nadmuchujesz układ dwukrotnie, po prostu sprawdź, czy napompujesz tylko raz.
źródło
Zagnieżdżone fragmenty nie są obecnie obsługiwane. Wypróbuj pakiet wsparcia, wersja 11 .
źródło
Czy próbowałeś odwołać się do swojej
MapFragment
klasy niestandardowej w pliku układu?źródło
Jeśli użyjesz tylko odpowiedzi Vidara Wahlberga, podczas otwierania innej aktywności (na przykład) i powrotu do mapy pojawi się błąd. Lub w moim przypadku otwórz inną aktywność, a następnie z nowej aktywności ponownie otwórz mapę (bez użycia przycisku Wstecz). Ale kiedy połączysz rozwiązanie Vidar Wahlberg i rozwiązanie Matt, nie będziesz mieć wyjątków.
układ
Fragment
źródło
Miałem to w viewPager, a awaria spowodowana była tym, że każdy fragment musiał mieć własny tag, duplikaty tagów lub identyfikatory dla tego samego fragmentu nie są dozwolone.
źródło
Myślę, że było kilka błędów w poprzedniej wersji biblioteki App-Compat dla Fragmentu potomnego. Próbowałem @Vidar Wahlberg i @ Matt, że nie działały dla mnie. Po zaktualizowaniu biblioteki appcompat mój kod działa idealnie bez żadnego dodatkowego wysiłku.
źródło
Należy zwrócić uwagę na to, że aplikacja ulegnie awarii w jednym z dwóch przypadków: -
Oto przykładowy fragment kodu do prawidłowego korzystania z MapView
XML
Wynik wygląda następująco: -
Mam nadzieję, że to komuś pomoże.
źródło
W tym rozwiązaniu nie trzeba brać zmiennej statycznej;
źródło
Jestem trochę spóźniony na przyjęcie, ale brak odpowiedzi pomógł mi w moim przypadku. Używałem mapy Google jako SupportMapFragment i PlaceAutocompleteFragment w moim fragmencie. Jak wszystkie odpowiedzi wskazywały na to, że problem polega na tym, że SupportMapFragment jest mapą do odtworzenia i przerysowania. Ale po wykopaniu dowiedziałem się, że mój problem był faktycznie z PlaceAutocompleteFragment
Oto więc skuteczne rozwiązanie dla tych, którzy napotykają ten problem z powodu SupportMapFragment i SupportMapFragment
A w onDestroyView wyczyść SupportMapFragment i SupportMapFragment
źródło
Spróbuj ustawić identyfikator (android: id = "@ + id / map_dialog") dla układu nadrzędnego mapView. Pracuje dla mnie.
źródło
Każdy, kto tu teraz przychodzi i ma ten typ błędu podczas otwierania tego
Dialog
lub innego miejscaFragment
w Miejscach GoogleAutocompleteSupportFragment
, wypróbuj ten jeden wiersz (nie wiem, jak to bezpieczne, ale działa dla mnie):autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();
zanim odrzucisz / zniszczysz swój fragment.
źródło
Dlaczego nie wstawisz mapy za pomocą obiektu MapView zamiast MapFragment? Nie jestem pewien, czy są jakieś ograniczenia w MapView, chociaż uznałem to za pomocne.
źródło