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
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.
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);
}
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.
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();
}
}
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;
}
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
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.
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.
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
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.
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:
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");
}
});
}
}
}
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.
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());
}
});
}`
Legen Sie für Ihr mapView-übergeordnetes Layout eine ID (Android: id = "@ + id/maps_dialog") fest. Funktioniert bei mir.
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.
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;
}
}
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"
/>
Ich hoffe es wird jemandem helfen.
Geschachtelte Fragmente werden derzeit nicht unterstützt . Try Support Package, Revision 11 .
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>
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();
}