我有一个带有三个选项卡的应用程序。
每个选项卡都有自己的布局.xml文件。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_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>
相关类: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
这里需要注意的是,你的应用程序在两种情况下都会严重崩溃:-
1) In order to reuse fragment with Maps again MapView Fragment must be removed when
your fragment showing Maps got replaced with other fragment in onDestroyView callback.
else when you try to inflate same fragment twice Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment error will happen.
2) Secondly you must not mix app.Fragment operations with
android.support.v4.app.Fragment API operations eg.do not use
android.app.FragmentTransaction to remove v4.app.Fragment type
MapView Fragment. Mixing this will again result into crash from fragment side.
下面是正确使用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"
/>
结果如下所示:-
希望它能帮助到某人。
问题是你试图做的事情不应该被做。你不应该让碎片在其他碎片中膨胀。来自Android的文档:
注意:不能在布局时将布局膨胀为片段
包含<fragment>。只有在添加时才支持嵌套片段
动态地连接到一个片段。
虽然您可以使用这里提供的技巧来完成任务,但我强烈建议您不要这样做。当你试图为包含另一个片段的片段膨胀布局时,不可能确定这些黑客将处理每个新的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_width="match_parent"
android:layout_height="match_parent"
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);
}