I have an application with three tabs. 我有一个带有三个标签的应用程序。
Each tab has its own layout .xml file. 每个选项卡都有其自己的布局.xml文件。 The main.xml has its own map fragment. main.xml有自己的地图片段。 It's the one that shows up when the application first launches. 这是在应用程序首次启动时显示的内容。
Everything works fine except for when I change between tabs. 一切正常,除了我在选项卡之间切换时。 If I try to switch back to the map fragment tab, I get this error. 如果尝试切换回地图片段选项卡,则会收到此错误。 Switching to and between other tabs works just fine. 切换到其他选项卡之间即可。
What could be wrong here? 这有什么问题吗?
This is my main class and my main.xml, as well as a relevant class that I use ( you will also find the error log at the bottom ) 这是我的主类和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 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 ) 相关类(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
#1楼
参考:https://stackoom.com/question/x5sU/重复ID-标签null或父ID-以及com-google-android-gms-maps-MapFragment的另一个片段
#2楼
Nested fragments are not currently supported. 目前不支持嵌套片段。 Try Support Package, revision 11 . 尝试使用支持包,修订版11 。
#3楼
I had the same issue and was able to resolve it by manually removing the MapFragment
in the onDestroy()
method of the Fragment
class. 我遇到了同样的问题,并且能够通过在Fragment
类的onDestroy()
方法中手动删除MapFragment
来解决此问题。 Here is code that works and references the MapFragment
by ID in the XML: 这是可以工作的代码,并通过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();
}
If you don't remove the MapFragment
manually, it will hang around so that it doesn't cost a lot of resources to recreate/show the map view again. 如果您不手动删除MapFragment
,它会在周围徘徊,这样就不会花费大量资源来重新创建/显示地图视图。 It seems that keeping the underlying MapView
is great for switching back and forth between tabs, but when used in fragments this behavior causes a duplicate MapView
to be created upon each new MapFragment
with the same ID. 似乎保留基础MapView
非常适合在选项卡之间来回切换,但是当在片段中使用此行为时,将导致在具有相同ID的每个新MapFragment
上创建重复的MapView
。 The solution is to manually remove the MapFragment
and thus recreate the underlying map each time the fragment is inflated. 解决方案是手动删除MapFragment
并在每次增加片段时重新创建基础地图。
I also noted this in another answer [ 1 ]. 我也在另一个答案[ 1 ]中指出了这一点。
#4楼
The answer Matt suggests works, but it cause the map to be recreated and redrawn, which isn't always desirable. 马特(Matt)提出的答案是可行的,但这会导致重新创建和重绘地图,但这并不总是令人满意的。 After lots of trial and error, I found a solution that works for me: 经过大量的反复试验,我找到了一个适合我的解决方案:
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;
}
For good measure, here's "map.xml" (R.layout.map) with R.id.mapFragment (android:id="@+id/mapFragment"): 为了达到良好的效果,这是带有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_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>
I hope this helps, but I can't guarantee that it doesn't have any adverse effects. 我希望这会有所帮助,但我不能保证它不会带来任何不利影响。
Edit: There were some adverse effects, such as when exiting the application and starting it again. 编辑:有一些不利影响,例如退出应用程序并重新启动时。 Since the application isn't necessarily completely shut down (but just put to sleep in the background), the previous code i submitted would fail upon restarting the application. 由于应用程序不一定完全关闭(而是在后台进入睡眠状态),因此我提交的先前代码在重新启动应用程序时将失败。 I've updated the code to something that works for me, both going in & out of the map and exiting and restarting the application, I'm not too happy with the try-catch bit, but it seem to work well enough. 我已经将代码更新为对我有用的东西,无论是进出地图还是退出并重新启动应用程序,我对try-catch位都不是很满意,但是它看起来已经足够好了。
When looking at the stack trace it occurred to me that I could just check if the map fragment is in the FragmentManager, no need for the try-catch block, code updated.
当查看堆栈跟踪时,我想到我可以检查映射片段是否在FragmentManager中,不需要try-catch块,代码已更新。
More edits: Turns out you need that try-catch after all. 更多编辑:事实证明,您毕竟需要try-catch。 Just checking for the map fragment turned out not to work so well after all. 仅仅检查地图片段竟然不能很好地工作。 Blergh. Blergh。
#5楼
I would recommend replace()
rather than attach()
/ detach()
in your tab handling. 我建议您在标签处理中使用replace()
而不是attach()
/ detach()
。
Or, switch to ViewPager
. 或者,切换到ViewPager
。 Here is a sample project showing a ViewPager
, with tabs, hosting 10 maps. 这是一个示例项目,显示一个ViewPager
,带有标签,可托管10张地图。
#6楼
Have you been trying to reference your custom MapFragment
class in the layout file? 您是否试图在布局文件中引用自定义MapFragment
类?
<?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>