我有一个带有三个标签的应用程序 .
每个选项卡都有自己的布局.xml文件 . main.xml有自己的 Map 片段 . 这是应用程序首次启动时显示的那个 .
除了在标签之间切换时,一切正常 . 如果我尝试切换回 Map 片段选项卡,我会收到此错误 . 切换到其他选项卡之间和之间的工作正常 .
这可能有什么问题?
这是我的主类和我的main.xml,以及我使用的相关类(你也会在底部找到错误日志)
main class
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>
relevant class ( 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
21 回答
全局声明SupportMapFragment对象
在onCreateView()方法下面放置代码
在onDestroyView()下面放置代码
在你的xml文件中放置代码
上面的代码解决了我的问题,它工作正常
我建议您在标签处理中使用
replace()
而不是attach()
/detach()
.或者,切换到
ViewPager
. Here is a sample project显示ViewPager
,带标签,托管10张 Map .Matt建议的答案是有效的,但它会导致 Map 被重新创建并重新绘制,这并不总是令人满意的 . 经过大量的反复试验,我发现了一个适合我的解决方案:
为了更好地衡量,这里是带有R.id.mapFragment(android:id =“@ id / mapFragment”)的“map.xml”(R.layout.map):
我希望这有帮助,但我不能保证它没有任何不利影响 .
Edit: 存在一些不利影响,例如退出应用程序并再次启动时 . 由于应用程序没有将代码更新为适合我的内容,无论是进出 Map 还是退出并重新启动应用程序,我对try-catch位都不太满意,但它似乎运行良好足够 . 在查看堆栈跟踪时,我发现我可以检查映射片段是否在FragmentManager中,不需要try-catch块,代码已更新 .
More edits: 毕竟你需要那个try-catch . 毕竟,检查 Map 片段结果不是很好 . Blergh .
问题是你要做的事情不应该是在其他片段内膨胀片段 . 来自Android的documentation:
虽然您可以通过此处提供的黑客来完成任务,但我强烈建议您不要这样做 . 当你试图给包含另一个片段的片段的布局充气时,不可能确定这些黑客会处理每个新的Android操作系统所做的事情 .
将片段添加到另一个片段的唯一Android支持方式是通过子片段管理器中的事务 .
只需将XML布局更改为空容器(如果需要,添加ID):
然后在Fragment
onViewCreated(View view, @Nullable Bundle savedInstanceState)
方法中:我有同样的问题,并能够通过手动删除
Fragment
类的onDestroy()
方法中的MapFragment
来解决它 . 以下代码可以工作并在XML中引用MapFragment
ID:如果您不手动删除
MapFragment
,它将会挂起,以便重新创建/显示 Map 视图不会花费大量资源 . 似乎保持底层MapView
非常适合在选项卡之间来回切换,但是当在片段中使用时,此行为会导致在具有相同ID的每个新MapFragment
上创建重复的MapView
. 解决方案是手动删除MapFragment
,从而在每次片段膨胀时重新创建基础映射 .我在另一个答案[1]中也注意到了这一点 .
这是我的答案:
1,创建如下的布局xml:
2,在Fragment类中,以编程方式添加谷歌 Map .
正如@Justin Breitfeller所提到的,@ Vidar Wahlberg解决方案是一个黑客攻击,可能在未来的Android版本中无效 .
@Vidar Wahlberg执行黑客攻击,因为其他解决方案可能"cause the map to be recreated and redrawn, which isn't always desirable" . 可以通过维护旧的 Map 片段来防止 Map 重绘,而不是每次都创建新的实例 .
@Matt解决方案对我不起作用(IllegalStateException)
引自@Justin Breitfeller,"You cannot inflate a layout into a fragment when that layout includes a . Nested fragments are only supported when added to a fragment dynamically."
我的解决方案
我今天已经失去了几个小时才找到原因,幸运的是这个问题不是因为MapFragment实现,遗憾的是,这不起作用,因为嵌套片段只能通过rev 11的支持库来支持 .
我的实现有一个带有操作栏的活动(在选项卡模式下),有两个选项卡(没有viewpager),一个有 Map ,另一个有条目列表 . 当然,我一直很天真地在我的标签片段中使用MapFragment,等等,每当我切换回map-tab时应用程序就崩溃了 .
(如果我的制表符片段会膨胀包含任何其他片段的任何布局,我也会遇到同样的问题) .
一种选择是使用MapView(而不是MapFragment),虽然有一些开销(请参阅MapView Docs作为layout.xml中的替代品,另一种选择是使用rev.11中的support-library,但之后采用编程方法)嵌套片段既不通过布局支持 . 或者只是通过明确地破坏片段以编程方式解决(如Matt / Vidar的答案),顺便说一句:使用MapView(选项1)实现相同的效果 .
但实际上,我不想在每次标签离开时松开 Map ,也就是说,我只想在活动结束时将其保留在内存中并进行清理,所以我决定在标签时隐藏/显示 Map ,请参阅FragmentTransaction / hide
对于那些仍然遇到这个问题的人来说,确保你没有通过选项卡中的Map获得此错误的最佳方法是使片段扩展
SupportMapFragment
而不是在用于Tab的片段内嵌套SupportMapFragment
.我刚刚使用带有
FragmentPagerAdapter
的ViewPager
和第三个Tab中的SupportMapFragment .这是一般结构,注意没有必要覆盖
onCreateView()
方法,并且不需要为任何布局xml充气:结果:
这是我用来测试的完整类代码,其中包括用于前两个Tabs的占位符Fragment,以及用于第三个Tab的Map Fragment:
另一种方案:
就是这样,如果不是null,你不需要重新初始化它从父项中删除是不必要的步骤 .
我尊重所有的答案,但我找到了这个单线解决方案:如果n是标签数量,那么:
示例:如果提到:
View pager实现了一个队列,因此您不必让它删除该片段 . onCreateView只被调用一次 .
目前不支持嵌套片段 . 试试Support Package, revision 11 .
您是否一直在尝试在布局文件中引用自定义
MapFragment
类?如果您只使用Vidar Wahlberg答案,则在打开其他活动(例如)并返回 Map 时会出现错误 . 或者在我的情况下打开其他活动然后再从新活动打开 Map (不使用后退按钮) . 但是当你将Vidar Wahlberg解决方案和Matt解决方案结合起来时,你就没有例外 .
布局
分段
我在viewPager中有这个,崩溃是因为任何片段都必须有自己的标签,不允许使用相同片段的重复标签或ID .
我认为以前的App-Compat lib中存在一些针对子Fragment的错误 . 我试过@Vidar Wahlberg和@Matt的ans他们没有为我工作 . 更新appcompat库后,我的代码运行完美,无需任何额外的努力 .
Things to Note here is your app will crash badly in either of two cases:-
以下是正确使用MapView的示例代码段
XML
结果如下所示: -
希望它会帮助某人 .
在此解决方案中,您不需要采用静态变量;
我参加聚会有点晚了,但这些答案中没有一个对我有帮助 . 我在我的片段中使用Google Map 作为 SupportMapFragment 和 PlaceAutocompleteFragment . 正如所有答案都指出了这一事实问题是SupportMapFragment是要重新创建和重绘的 Map . 但挖掘后发现我的问题实际上是 PlaceAutocompleteFragment
所以这是因为 SupportMapFragment 和 SupportMapFragment 而面临这个问题的人的工作解决方案
在onDestroyView中清除SupportMapFragment和SupportMapFragment
尝试为mapView父布局设置id(android:id =“@ id / maps_dialog”) . 适合我 .
为什么不使用MapView对象而不是MapFragment插入 Map ?我不确定MapView是否有任何限制,但我觉得它很有帮助 .