ID、标签 null 或父 ID 与 com.google.android.gms.maps.MapFragment 的另一个片段重复
Posted
技术标签:
【中文标题】ID、标签 null 或父 ID 与 com.google.android.gms.maps.MapFragment 的另一个片段重复【英文标题】:Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment 【发布时间】:2012-12-14 13:42:33 【问题描述】:我有一个包含三个选项卡的应用程序。
每个选项卡都有自己的布局 .xml 文件。 main.xml 有自己的地图片段。它是应用程序首次启动时显示的那个。
除了我在标签之间切换时,一切都很好。如果我尝试切换回地图片段选项卡,则会收到此错误。切换到其他选项卡并在其他选项卡之间切换效果很好。
这里有什么问题?
这是我的主类和我的main.xml,以及我使用的一个相关类(你也会在底部找到错误日志)
主类
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_
android:layout_
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_
android:layout_ />
</LinearLayout>
相关类(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();
错误
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
【问题讨论】:
试试这个 - return super.onCreateView(inflater, container, savedInstanceState);而不是 super.onCreateView(inflater, container, savedInstanceState); return inflater.inflate(R.layout.main, container, false); 当 savedInstanceState 不为空时,你不应该添加你的片段。 看看这条评论。它可能对您有所帮助:***.com/questions/15562416/… 请更改接受的答案!您选择了一个非常糟糕的答案,这会造成内存泄漏!正确答案是这样的:***.com/a/19815266/902276 【参考方案1】:马特建议的答案有效,但它会导致重新创建和重新绘制地图,这并不总是可取的。 经过大量的试验和错误,我找到了一个适合我的解决方案:
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;
为了更好的衡量,这里是带有 R.id.mapFragment (android:id="@+id/mapFragment") 的 "map.xml" (R.layout.map):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapLayout"
android:layout_
android:layout_ >
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragment"
android:layout_
android:layout_
class="com.google.android.gms.maps.SupportMapFragment" />
</LinearLayout>
我希望这会有所帮助,但我不能保证它没有任何不良影响。
编辑:有一些不利影响,例如退出应用程序并重新启动时。由于应用程序不一定完全关闭(而只是在后台进入睡眠状态),我之前提交的代码在重新启动应用程序时会失败。我已经将代码更新为对我有用的东西,进出地图以及退出和重新启动应用程序,我对 try-catch 位不太满意,但它似乎工作得很好。 在查看堆栈跟踪时,我突然想到我可以检查地图片段是否在 FragmentManager 中,不需要 try-catch 块,代码已更新。
更多修改: 事实证明,您毕竟需要 try-catch。毕竟只是检查地图片段并不能很好地工作。布莱尔。
【讨论】:
这是一个错误的答案-1!您正在使用视图的静态修饰符泄漏活动。这个问题的根本原因可能是另一个泄露的活动,它不能被垃圾收集,因为你保持一个指向它的强引用。如果抛出 InflateException,您正在使用具有已销毁活动上下文的视图!最好在你的应用程序中找到其他内存泄漏,这将解决所有问题。 我想我们可以使用 WeakReference 来避免内存泄漏? 这对我也有用 +1。您没有义务使用视图的静态引用 不要这样做!!!它会导致内存泄漏。发生这种情况的唯一原因是您将 XML 中的一个片段膨胀到另一个片段中。你不应该那样做!您应该使用 ChildFragmentManager 并在 onViewCreated() 中添加片段! 是的,这确实会泄露活动。在***.com/a/27592123/683763 中找到了一个可行的解决方案。这个想法是在onDestroyView
方法中手动删除SupportMapFragment
。【参考方案2】:
问题是你试图做的事情不应该做。您不应该在其他片段中膨胀片段。来自安卓的documentation:
注意:当该布局时,您不能将布局膨胀为片段 包括一个。仅在添加时才支持嵌套片段 动态到一个片段。
虽然您可以使用此处介绍的技巧完成任务,但我强烈建议您不要这样做。当您尝试为包含另一个片段的片段扩展布局时,无法确定这些黑客将处理每个新的 Android 操作系统所做的事情。
将一个片段添加到另一个片段的唯一 Android 支持的方法是通过来自子片段管理器的事务。
只需将您的 XML 布局更改为一个空容器(如果需要,添加一个 ID):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragmentContainer"
android:layout_
android:layout_
android:orientation="vertical" >
</LinearLayout>
然后在 Fragment onViewCreated(View view, @Nullable Bundle savedInstanceState)
方法中:
@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);
【讨论】:
有关以编程方式创建地图片段和初始化地图的示例,请参阅 ***.com/questions/13733299/…。 另请参阅***.com/questions/19239175/…,了解子片段支持中明显错误的解决方法。 我在使用 XML 中定义的SupportMapFragment
时在 4.3
上遇到了这个问题。动态创建片段并将其注入容器视图解决了该问题。看到这个SO answer。
非常有用的答案。我们如何对 onCreate() 主函数进行回调?
根据文档,使用SupportMapFragment.newInstance();
developers.google.com/maps/documentation/android-api/map【参考方案3】:
我遇到了同样的问题,并且能够通过手动删除 Fragment
类的 onDestroy()
方法中的 MapFragment
来解决它。下面是工作的代码,并通过 XML 中的 ID 引用 MapFragment
:
@Override
public void onDestroyView()
super.onDestroyView();
MapFragment f = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map);
if (f != null)
getFragmentManager().beginTransaction().remove(f).commit();
如果您不手动删除MapFragment
,它会一直挂起,这样就不会花费大量资源来重新创建/显示地图视图。似乎保留底层MapView
非常适合在选项卡之间来回切换,但是当在片段中使用时,这种行为会导致在每个具有相同ID 的新MapFragment
上创建一个重复的MapView
。解决方案是手动移除MapFragment
,从而在每次片段膨胀时重新创建底层地图。
我在另一个答案 [1] 中也注意到了这一点。
【讨论】:
当点击设备的主页按钮时,应用会崩溃,开发者选项中有“不保留活动”。 这可行,但如果我旋转屏幕,我的应用程序会因以下异常而崩溃:原因:java.lang.IllegalStateException:onSaveInstanceState 后无法执行此操作 可能有兴趣的朋友可以通过在fragment的构造函数中存储初始方向并在方向没有改变的情况下才调用上述事务来消除屏幕旋转引起的异常。如果已更改,则无需为重复的 ID 苦苦挣扎,因为在旋转后重新创建视图时它不存在。如果马特不介意,我可以编辑答案并提供代码 sn-p。 嗯,对我来说,地图片段没有被删除。我一定是做错了什么,但如果我调用 getActivity().getSupportFragmentManager().getFragments(),片段在调用 remove(f).commit 之后仍然存在。有谁知道为什么? (我用 getSupportFragManager 替换了 getFragManager) commitAllowingStateLoss 将避免 IllegalStateException 异常。【参考方案4】:这是我的回答:
1、创建如下布局xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map_container"
android:layout_
android:layout_>
</FrameLayout>
2、在Fragment类中,以编程方式添加谷歌地图。
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();
【讨论】:
mMapFragment.getMap();
返回null
。知道为什么吗?
@AnasAzeem ,以编程方式创建片段可以完全解决问题,您不必为解决方案获取 mMap。
@AnasAzeem 您应该使用 get getMapAsync ,它将在初始化后(在后台)正确返回地图实例。这是使用谷歌地图的“正确”方式,与片段无关【参考方案5】:
-
正如 @Justin Breitfeller 所提到的,@Vidar Wahlberg 解决方案是一种 hack,可能无法在未来的 Android 版本中使用。
@Vidar Wahlberg 喜欢破解,因为其他解决方案可能“导致重新创建和重新绘制地图,这并不总是可取的”。可以通过维护旧地图片段来防止地图重绘,而不是每次都创建一个新实例。
@Matt 解决方案对我不起作用 (IllegalStateException)
正如 @Justin Breitfeller 所引用的,“当布局包含 .
我的解决方案:
@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;
【讨论】:
感谢@Desmond 您的解决方案对我来说完美无缺。您唯一需要记住的是不要在布局中创建地图。此解决方案中的地图创建嵌入在代码中,因此将全局声明 SupportMapFragment 对象
private SupportMapFragment mapFragment;
在 onCreateView() 方法中放入下面的代码
mapFragment = (SupportMapFragment) getChildFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
在 onDestroyView() 中放入以下代码
@Override
public void onDestroyView()
super.onDestroyView();
if (mapFragment != null)
getFragmentManager().beginTransaction().remove(mapFragment).commit();
在你的xml文件中放下面的代码
<fragment
android:id="@+id/map"
android:name="com.abc.Driver.fragment.FragmentHome"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_
android:layout_
/>
以上代码解决了我的问题,并且运行良好
【讨论】:
【参考方案7】:我会在标签处理中推荐replace()
而不是attach()
/detach()
。
或者,切换到ViewPager
。 Here is a sample project 显示 ViewPager
,带有选项卡,托管 10 个地图。
【讨论】:
但是如果我们使用分离窗口而不是地图问题发生时,替换会给出 outofmemry 错误。【参考方案8】:另一种解决方案:
if (view == null)
view = inflater.inflate(R.layout.nearbyplaces, container, false);
就是这样,如果不为空,则无需重新初始化,从父级中删除是不必要的步骤。
【讨论】:
这是我的情况的最佳解决方案。由于我的导航图使用了两个片段,我得到了这个错误,这解决了我的问题。 这是我的情况的最佳解决方案。由于我的导航图使用了两个片段,我得到了这个错误,这解决了我的问题。【参考方案9】:您正在返回或膨胀布局两次,只需检查您是否只膨胀一次。
【讨论】:
伟大的收获!我会找几个小时,但没有注意到这一点。谢谢@MahdiGiveie【参考方案10】:我今天花了几个小时寻找原因,幸运的是这个问题不是因为 MapFragment 实现,幸运的是,这不起作用,因为嵌套片段仅通过 rev 11 的支持库支持。
我的实现有一个带有操作栏的活动(在选项卡模式下),有两个选项卡(没有查看器),一个有地图,另一个有一个条目列表。当然,我在我的标签片段中使用 MapFragment 是非常天真的,等等,每次我切换回地图标签时,应用程序都会崩溃。
(如果我的标签片段会膨胀包含任何其他片段的任何布局,我也会遇到同样的问题)。
一种选择是使用 MapView(而不是 MapFragment),尽管有一些开销(请参阅 MapView Docs 作为 layout.xml 中的替代品,另一种选择是使用从 rev. 11 开始的 support-library 但是然后采用编程方法,因为布局既不支持嵌套片段。或者只是通过显式销毁片段以编程方式解决(如 Matt / Vidar 的答案),顺便说一句:使用 MapView 实现相同的效果(选项 1)。
但实际上,我不想每次 tab 离开时都松开地图,也就是说,我想把它保存在内存中,只在活动关闭时清理,所以我决定在 tab 时简单地隐藏/显示地图,见FragmentTransaction / hide
【讨论】:
【参考方案11】:对于那些仍然遇到这个问题的人,确保在 Tab 中使用 Map 不会出现此错误的最佳方法是使 Fragment 扩展 SupportMapFragment
而不是在内部嵌套 SupportMapFragment
用于 Tab 的片段。
我刚刚使用ViewPager
和FragmentPagerAdapter
完成了这项工作,SupportMapFragment 在第三个选项卡中。
这里是大体结构,注意不需要重写onCreateView()
方法,也不需要膨胀任何布局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)));
);
结果:
这是我用来测试的完整类代码,其中包括用于前两个选项卡的占位符片段,以及用于第三个选项卡的地图片段:
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");
);
【讨论】:
【参考方案12】:我尊重所有答案,但我找到了这个唯一的解决方案: 如果 n 是选项卡数,则:
mViewPager.setOffscreenPageLimit(n);
示例: 如果提到:
mViewPager.setOffscreenPageLimit(2);
View pager 实现了一个队列,因此您不必让它删除该片段。 onCreateView 只被调用一次。
【讨论】:
【参考方案13】:我参加聚会有点晚了,但这些答案都没有帮助我。我在我的片段中都使用 Google 地图作为 SupportMapFragment 和 PlaceAutocompleteFragment。由于所有答案都指出问题在于 SupportMapFragment 是要重新创建和重绘的地图。但是在挖掘后发现我的问题实际上是 PlaceAutocompleteFragment
因此,对于那些因 SupportMapFragment 和 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);
并在 onDestroyView 中清除 SupportMapFragment 和 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;
【讨论】:
【参考方案14】:目前不支持嵌套片段。 试试Support Package, revision 11。
【讨论】:
【参考方案15】:您是否尝试在布局文件中引用您的自定义 MapFragment
类?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
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_
android:layout_ />
</LinearLayout>
【讨论】:
能否请您发布自定义地图片段 com.nfc.demo.MapFragment 的代码 我不是代码的作者 - 我只是使用了有问题的代码。你得问问赫尔曼。【参考方案16】:如果您只使用 Vidar Wahlberg 答案,则在打开其他活动(例如)并返回地图时会出现错误。或者在我的情况下打开其他活动,然后从新活动再次打开地图(不使用后退按钮)。 但是当你结合 Vidar Wahlberg 解决方案和 Matt 解决方案时,你不会有例外。
布局
<com.example.ui.layout.MapWrapperLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:id="@+id/map_relative_layout">
<RelativeLayout
android:layout_
android:layout_
android:id="@+id/root">
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:layout_
android:layout_
class="com.google.android.gms.maps.SupportMapFragment" />
</RelativeLayout>
</<com.example.ui.layout.MapWrapperLayout>
片段
@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();
【讨论】:
【参考方案17】:我在 viewPager 中有这个,崩溃是因为任何片段都必须有自己的标签,不允许相同片段的重复标签或 ID。
【讨论】:
【参考方案18】:我认为以前的 App-Compat 库中存在一些针对子片段的错误。我尝试了@Vidar Wahlberg 和@Matt 的答案,它们对我不起作用。更新 appcompat 库后,我的代码无需任何额外工作即可完美运行。
【讨论】:
【参考方案19】:这里需要注意的是,您的应用会在以下两种情况下严重崩溃:-
1) 为了在 Maps 中再次使用 Fragment,MapView Fragment 必须在以下情况下被移除 您显示地图的片段已替换为 onDestroyView 回调中的其他片段。
否则,当您尝试将同一片段膨胀两次时重复 ID、标签 null 或父 ID 与 com.google.android.gms.maps.MapFragment 的另一个片段会发生错误。
2) 其次,您不能将 app.Fragment 操作与 android.support.v4.app.Fragment API 操作 eg.不要使用 android.app.FragmentTransaction 移除 v4.app.Fragment 类型 地图视图片段。混合这将再次导致片段方面的崩溃。
这里是正确使用 MapView 的示例代码 sn-p
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_
android:layout_
/>
结果如下所示:-
希望对某人有所帮助。
【讨论】:
【参考方案20】:在这个解决方案中你不需要使用静态变量;
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());
);
`
【讨论】:
【参考方案21】:尝试为您的 mapView 父布局设置一个 id (android:id="@+id/maps_dialog")。对我有用。
【讨论】:
【参考方案22】:现在来这里的任何人在使用 Google Places AutocompleteSupportFragment
打开 Dialog
或其他 Fragment
时遇到这种类型的错误,试试这个单线(我不知道这有多安全,但它有效对我来说):
autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();
在您关闭/销毁您的片段之前。
【讨论】:
【参考方案23】:<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_ >
<com.google.android.gms.maps.MapView
android:id="@+id/mapview"
android:layout_
android:layout_
android:layout_alignParentTop="true"
android:layout_alignRight="@+id/textView1"
android:layout_marginRight="15dp" >
</com.google.android.gms.maps.MapView>
为什么不使用 MapView 对象而不是 MapFragment 插入地图?我不确定 MapView 是否有任何限制,尽管我发现它很有帮助。
【讨论】:
抱歉,我知道这是已弃用的 Google Maps API 以前的版本以上是关于ID、标签 null 或父 ID 与 com.google.android.gms.maps.MapFragment 的另一个片段重复的主要内容,如果未能解决你的问题,请参考以下文章
MySQL - 选择共享相同 ID 的所有行,每行包含等于 null 的列