Dupliquer ID, tag null, ou parent id avec un autre fragment pour com.Google.Android.gms.cartographie.MapFragment
j'ai une application avec trois onglets.
chaque onglet a sa propre disposition .fichier xml. Principal.xml a son propre fragment de carte. C'est celui qui apparaît lors du premier lancement de l'application.
Tout fonctionne bien sauf quand je change d'onglet. Si j'essaie de revenir à la carte fragment onglet, j'ai cette erreur. De commutation et entre autres onglets fonctionne très bien.
Qu'est-ce qui ne va pas ici?
C'est ma classe principale et mon principal.xml, ainsi qu'une classe que j'utilise ( vous trouverez également le journal des erreurs en bas )
classe principale
package com.nfc.demo;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Toast;
public class NFCDemoActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar
.newTab()
.setText("Map")
.setTabListener(
new TabListener<MapFragment>(this, "map",
MapFragment.class)));
bar.addTab(bar
.newTab()
.setText("Settings")
.setTabListener(
new TabListener<SettingsFragment>(this, "settings",
SettingsFragment.class)));
bar.addTab(bar
.newTab()
.setText("About")
.setTabListener(
new TabListener<AboutFragment>(this, "about",
AboutFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
// setContentView(R.layout.main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
public static class TabListener<T extends Fragment> implements
ActionBar.TabListener {
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public TabListener(Activity activity, String tag, Class<T> clz) {
this(activity, tag, clz, null);
}
public TabListener(Activity activity, String tag, Class<T> clz,
Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
// Check to see if we already have a fragment for this tab,
// probably from a previously saved state. If so, deactivate
// it, because our initial state is that a tab isn't shown.
mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
FragmentTransaction ft = mActivity.getFragmentManager()
.beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mFragment == null) {
mFragment = Fragment.instantiate(mActivity, mClass.getName(),
mArgs);
ft.add(android.R.id.content, mFragment, mTag);
} else {
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT)
.show();
}
}
}
principal.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>
classe pertinente ( MapFragment.java)
package com.nfc.demo;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.main, container, false);
}
public void onDestroy() {
super.onDestroy();
}
}
erreur
android.view.InflateException: Binary XML file line #7:
Error inflating class fragment
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at com.nfc.demo.MapFragment.onCreateView(MapFragment.java:15)
at android.app.Fragment.performCreateView(Fragment.java:1695)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:885)
at android.app.FragmentManagerImpl.attachFragment(FragmentManager.java:1255)
at android.app.BackStackRecord.run(BackStackRecord.java:672)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435)
at android.app.FragmentManagerImpl.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
20 réponses
la réponse Matt suggère des œuvres, mais cela fait que la carte est recréée et redessinée, ce qui n'est pas toujours souhaitable. Après beaucoup d'essais et d'erreurs, j'ai trouvé une solution qui fonctionne pour moi:
private static View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null)
parent.removeView(view);
}
try {
view = inflater.inflate(R.layout.map, container, false);
} catch (InflateException e) {
/* map is already there, just return view as it is */
}
return view;
}
pour faire bonne mesure, voici la carte.xml" (R. layout.carte) avec R. id.mapFragment (android: id= "@+id / mapFragment"):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
</LinearLayout>
j'espère que cela aidera, mais je ne peux pas garantir qu'il n'a pas d'effets indésirables.
Edit: Il y avait des effets indésirables, tels que lorsque vous quittez l'application et de commencer à nouveau. Puisque l'application n'est pas nécessairement complètement arrêté (mais simplement mis au sommeil dans l'arrière-plan), le code précédent que j'ai soumis échouerait en redémarrant la demande. J'ai mis à jour le code à quelque chose qui fonctionne pour moi, à la fois entrer et sortir de la carte et sortir et redémarrer l'application, Je ne suis pas trop heureux avec le try-catch bit, mais il semble bien fonctionner assez. en regardant la trace de la pile, il m'est venu à l'esprit que je pouvais juste vérifier si le fragment de carte est dans le FragmentManager, pas besoin pour le bloc try-catch, code mis à jour.
plus d'éditions: il S'avère que vous avez besoin de ce try-catch après tout. Juste vérifier pour le fragment de carte s'est avéré ne pas fonctionner si bien, après tout. Blergh.
le problème est que ce que vous essayez de faire ne devrait pas être fait. Tu ne devrais pas gonfler des fragments à l'intérieur d'autres fragments. De Android documentation :
Note: vous ne pouvez pas gonfler une mise en page dans un fragment lorsque cette mise en page inclut un < fragment>. Les fragments imbriqués ne sont supportés que s'ils sont ajoutés à un fragment dynamiquement.
Alors que vous pouvez être en mesure d'accomplir la tâche avec les hacks présentés ici, je vous conseille vivement de ne pas le faire. Il est impossible d'être sûr que ces hacks vont gérer ce que chaque nouvel OS Android fait quand vous essayez de gonfler un layout pour un fragment contenant un autre fragment.
la seule façon D'ajouter un fragment à un autre fragment est via une transaction du gestionnaire de fragment enfant.
il vous suffit de changer votre mise en page XML dans un conteneur vide (ajouter un ID si nécessaire):
<?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>
puis dans le Fragment onViewCreated(View view, @Nullable Bundle savedInstanceState)
méthode:
@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);
}
j'ai eu le même problème et j'ai pu le résoudre en enlevant manuellement le MapFragment
dans la méthode onDestroy()
de la classe Fragment
. Voici le code qui fonctionne et fait référence au MapFragment
par ID dans le XML:
@Override
public void onDestroyView() {
super.onDestroyView();
MapFragment f = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map);
if (f != null)
getFragmentManager().beginTransaction().remove(f).commit();
}
si vous ne retirez pas le MapFragment
manuellement, il va traîner autour de sorte qu'il ne coûte pas beaucoup de ressources pour recréer/montrer la vue de carte à nouveau. Il semble que garder le sous-jacent MapView
est grand pour passer d'un onglet à l'autre, mais lorsque ce comportement est utilisé dans les fragments, il provoque la création d'un duplicata MapView
sur chaque nouveau MapFragment
avec le même ID. La solution est d'enlever manuellement le MapFragment
et de recréer ainsi la carte sous-jacente chaque fois que le fragment est gonflé.
Je l'ai également noté dans une autre réponse [ 1 ].
C'est ma réponse:
1, Créer une mise en page xml comme suit:
<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, dans la classe Fragment, ajouter une carte google par programmation.
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();
}
}
- comme mentionné par @Justin Breitfeller, @Vidar Wahlberg solution est un piratage qui pourrait ne pas fonctionner dans la future version D'Android.
- @Vidar Wahlberg perfer un hack parce qu'une autre solution pourrait"faire que la carte soit recréée et redessinée, ce qui n'est pas toujours souhaitable". La redessination de la carte pourrait être évitée en maintenant l'ancien fragment de carte, plutôt que de créer une nouvelle instance à chaque fois.
- @Matt solution ne fonctionne pas pour moi (IllegalStateException)
- comme cité par @Justin Breitfeller, "vous ne pouvez pas gonfler une mise en page dans un fragment quand cette mise en page inclut A. Les fragments imbriqués ne sont supportés que s'ils sont ajoutés dynamiquement à un fragment."
Ma solution:
@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;
}
Déclarer SupportMapFragment objet à l'échelle mondiale
private SupportMapFragment mapFragment;
Dans onCreateView() méthode de mettre le code ci-dessous
mapFragment = (SupportMapFragment) getChildFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
Dans onDestroyView() mettre ci-dessous le code
@Override
public void onDestroyView() {
super.onDestroyView();
if (mapFragment != null)
getFragmentManager().beginTransaction().remove(mapFragment).commit();
}
dans votre fichier xml mettez le code ci-dessous
<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"
/>
code ci-dessus résolu mon problème et il fonctionne très bien
je recommanderais replace()
plutôt que attach()
/ detach()
dans votre manipulation de l'onglet.
Ou, le passage à ViewPager
. voici un exemple de projet montrant un ViewPager
, avec onglets, hébergement de 10 cartes.
j'ai perdu des heures aujourd'hui pour trouver la raison, heureusement ce problème n'est pas à cause de la mise en œuvre de MapFragment, malheureusement, cela ne fonctionne pas parce que les fragments imbriqués ne sont pris en charge que par la bibliothèque de soutien de rev 11.
mon implémentation a une activité avec actionbar (en mode tabbed) avec deux onglets (pas de viewpager), l'un ayant la carte et l'autre ayant une liste d'entrées. Bien sûr, J'ai été assez naïf d'utiliser MapFragment à l'intérieur de mon tab-fragments, et voila l'application s'est crashée à chaque fois que je suis revenu à map-tab.
(le même numéro que j'aurais aussi au cas où mon fragment d'onglet gonflerait n'importe quelle mise en page contenant n'importe quel autre fragment ).
une option est d'utiliser le MapView (au lieu du MapFragment), avec un peu de plafond cependant ( voir MapView Docs comme drop-in de remplacement dans le layout.xml, une autre option est d'utiliser support-library up à partir de rev. 11 mais puis prendre approche programmatique puisque les fragments imbriqués ne sont pas pris en charge par layout. Ou simplement en travaillant par programmation en détruisant explicitement le fragment (comme dans la réponse de Matt / Vidar), btw: le même effet est obtenu en utilisant le MapView (option 1).
mais en fait, je ne voulais pas perdre la carte à chaque fois que je l'affichais, c'est-à-dire que je voulais la garder en mémoire et la nettoyer seulement lorsque l'activité était proche, alors j'ai décidé de simplement cacher/montrer la carte tout en l'affichant, voir FragmentTransaction / masquer
pour ceux qui sont encore en cours d'exécution dans ce numéro, la meilleure façon de s'assurer que vous n'obtenez pas cette erreur avec une carte dans un onglet est de faire le Fragment étendre SupportMapFragment
au lieu de l'imbrication d'un SupportMapFragment
à l'intérieur du Fragment utilisé pour l'onglet.
je viens d'obtenir ce travail en utilisant un ViewPager
avec un FragmentPagerAdapter
, avec le SupportMapFragment dans le troisième onglet.
Voici la structure générale. onCreateView()
méthode, et il n'est pas nécessaire de gonfler toute disposition xml:
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)));
}
});
}
}
résultat:
voici le code de classe complet que j'ai utilisé pour tester avec, qui inclut le Fragment placeholder utilisé pour les deux premiers onglets, et le Fragment de carte utilisé pour le troisième onglet:
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");
}
});
}
}
}
une autre solution:
if (view == null) {
view = inflater.inflate(R.layout.nearbyplaces, container, false);
}
C'est ça, si ce n'est nul, vous n'avez pas besoin de le réinitialiser.
je respecte toutes les réponses mais j'ai trouvé cette solution: Si n est le nombre d'onglets alors:
mViewPager.setOffscreenPageLimit(n);
exemple: Dans le cas mentionné:
mViewPager.setOffscreenPageLimit(2);
Afficher pager met en place une file d'attente, vous n'avez pas à laisser enlever ce fragment. onreateview n'est appelé qu'une fois.
ne sont pas pris en charge actuellement. Essayez Support Package, révision 11 .
avez-vous essayé de faire référence à votre classe personnalisée MapFragment
dans le fichier de mise en page?
<?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>
si vous n'utilisez que la réponse de Vidar Wahlberg, vous obtenez une erreur lorsque vous ouvrez une autre activité (par exemple) et retournez à la carte. Ou dans mon cas, ouvrez une autre activité, puis à partir d'une nouvelle activité, Ouvrez à nouveau la carte( sans utiliser le bouton Précédent). Mais lorsque vous combinez la solution de Vidar Wahlberg et la solution de Matt, vous n'aurez pas d'exceptions.
mise en page
<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();
}
j'avais ceci dans viewPager et le crash était parce que n'importe quel fragment devait avoir sa propre étiquette, les étiquettes dupliquées ou les ids pour le même fragment ne sont pas autorisés.
je pense qu'il y avait quelques bugs dans la précédente application-Compat lib pour fragment d'enfant. J'ai essayé @ Vidar Wahlberg et @Matt et ils n'ont pas travaillé pour moi. Après avoir mis à jour la bibliothèque appcompat, mon code s'exécute parfaitement sans effort supplémentaire.
les choses à noter voici votre application va se planter mal dans l'un des deux cas:-
1) pour réutiliser le fragment MapView avec Maps again, le Fragment MapView doit être supprimé lorsque votre fragment montrant Maps a été remplacé par un autre fragment dans onDestroyView callback.
else quand vous essayez de gonfler le même fragment deux fois Duplicate ID, tag null, ou parent id avec un autre fragment pour COM.Google.Android.gms.cartographie.MapFragment erreur se produira.
2) Deuxièmement, vous ne devez pas mélanger app.Opérations de fragmentation avec Android.soutien.v4.App.Opérations de l'API de Fragment par ex. ne pas utiliser Android.App.FragmentTransaction pour supprimer v4.App.Fragment de type Fragment MapView. En mélangeant cela, on obtient à nouveau un crash du côté du fragment.
voici un exemple d'extrait de code pour une utilisation correcte de 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"
/>
j'Espère que ça aidera Quelqu'un.
dans cette solution vous n'avez pas besoin de prendre la variable statique;
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());
}
});
}`
je suis un peu en retard à la fête mais aucune de ces réponses m'a aidé dans mon cas. J'ai utilisé Google map comme SupportMapFragment et PlaceAutocompleteFragment tous les deux dans mon fragment. Comme toutes les réponses ont souligné le fait que le problème est avec SupportMapFragment étant la carte à recréer et redessiner.Mais après avoir creusé découvert que mon problème était en fait avec PlaceAutocompleteFragment
Voici donc la solution de travail pour ceux qui sont confrontés à ce problème en raison de SupportMapFragment et SupportMapFragment
//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);
}
Et dans onDestroyView effacer le SupportMapFragment et 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;
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.google.android.gms.maps.MapView
android:id="@+id/mapview"
android:layout_width="100dip"
android:layout_height="100dip"
android:layout_alignParentTop="true"
android:layout_alignRight="@+id/textView1"
android:layout_marginRight="15dp" >
</com.google.android.gms.maps.MapView>
pourquoi n'insérez-vous pas une carte en utilisant L'objet MapView au lieu de MapFragment ? Je ne suis pas sûr qu'il y ait des limites dans MapView,même si J'ai trouvé ça utile.