我有一个带有三个选项卡的应用程序。

每个选项卡都有自己的布局.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

当前回答

对于那些仍然遇到这个问题的人来说,确保您不会在Tab中的Map中得到这个错误的最好方法是使Fragment扩展SupportMapFragment,而不是在用于Tab的Fragment中嵌套SupportMapFragment。

我刚刚得到这个工作使用一个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");

                }
            });

        }
    }
}

其他回答

嵌套片段目前不受支持。 试试Support Package,修订版11。

如果你只使用Vidar Wahlberg的答案,当你打开其他活动(例如)并返回到地图时,你会得到错误。或者在我的情况下打开其他活动,然后从新的活动打开地图再次(不使用后退按钮)。 但是当你结合维达尔·沃尔伯格解决方案和马特解决方案时,你将没有例外。

布局

<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>

片段

@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();
}

这是我的回答:

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_width="match_parent"
android:layout_height="match_parent">
</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();
    }
} 

全局声明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_width="match_parent"
    android:layout_height="match_parent"
    />

上面的代码解决了我的问题,它的工作很好

As mentioned by @Justin Breitfeller, @Vidar Wahlberg solution is a hack which might not work in future version of Android. @Vidar Wahlberg perfer a hack because other solution might "cause the map to be recreated and redrawn, which isn't always desirable". Map redraw could be prevented by maintaining the old map fragment, rather than creating a new instance every time. @Matt solution doesn't work for me (IllegalStateException) As quoted by @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."

我的解决方案:

@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;
}