ChatGPT解决这个技术问题 Extra ChatGPT

ID、标签 null 或父 ID 与 com.google.android.gms.maps.MapFragment 的另一个片段重复

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

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

}

主要的.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
试试这个 - 返回 super.onCreateView(inflater, container, savedInstanceState);而不是 super.onCreateView(inflater, container, savedInstanceState); return inflater.inflate(R.layout.main, 容器, false);
当 savedInstanceState 不为空时,你不应该添加你的片段。
看看这个评论。它可能会对您有所帮助:stackoverflow.com/questions/15562416/…
请更改接受的答案!您选择了一个非常糟糕的答案,这会造成内存泄漏!正确答案是:stackoverflow.com/a/19815266/902276

V
Vidar Wahlberg

马特建议的答案有效,但它会导致重新创建和重新绘制地图,这并不总是可取的。经过大量的试验和错误,我找到了一个适合我的解决方案:

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

我希望这会有所帮助,但我不能保证它不会产生任何不利影响。

编辑:有一些不利影响,例如退出应用程序并重新启动时。由于应用程序不一定完全关闭(而只是在后台进入睡眠状态),我之前提交的代码在重新启动应用程序时会失败。我已经将代码更新为适合我的东西,进出地图以及退出和重新启动应用程序,我对 try-catch 位不太满意,但它似乎工作得很好。在查看堆栈跟踪时,我突然想到我可以检查地图片段是否在 FragmentManager 中,不需要 try-catch 块,代码已更新。

更多编辑:事实证明你毕竟需要那个 try-catch。毕竟只是检查地图片段并不能很好地工作。布莱尔。


这是一个错误的答案-1!您正在使用视图的静态修饰符泄漏活动。这个问题的根本原因可能是另一个泄露的活动,它不能被垃圾收集,因为你保持一个指向它的强引用。如果抛出 InflateException,您正在使用具有已销毁活动上下文的视图!最好在您的应用程序中找到其他内存泄漏,这将解决所有问题。
我想我们可以使用 WeakReference 来避免内存泄漏?
这对我也有用+1。您没有义务使用视图的静态参考
不要这样做!!!它会导致内存泄漏。发生这种情况的唯一原因是您将 XML 中的一个片段膨胀到另一个片段中。你不应该那样做!您应该使用 ChildFragmentManager 并在 onViewCreated() 中添加片段!
是的,这确实会泄露活动。在 stackoverflow.com/a/27592123/683763 中找到了一个可行的解决方案。这个想法是手动删除 onDestroyView 方法中的 SupportMapFragment
D
Daniele Segato

问题是你试图做的事情不应该被做。您不应该在其他片段中膨胀片段。来自 Android 的 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_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);
}

有关以编程方式创建地图片段和初始化地图的示例,请参阅 stackoverflow.com/questions/13733299/…
有关子片段支持中明显错误的解决方法,另请参阅 stackoverflow.com/questions/19239175/…
在使用 XML 中定义的 SupportMapFragment 时,我在 4.3 上遇到了这个问题。动态创建片段并将其注入容器视图解决了该问题。请参阅此SO answer
非常有用的答案。我们如何对 onCreate() 主函数进行回调?
根据文档,使用 SupportMapFragment.newInstance(); developers.google.com/maps/documentation/android-api/map
C
Community

我遇到了同样的问题,并且能够通过手动删除 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 后无法执行此操作
对于那些可能感兴趣的人,您可以通过将初始方向存储在片段的构造函数中并仅在方向未更改时调用上述事务来消除屏幕旋转引起的异常。如果已更改,则无需为重复的 ID 苦苦挣扎,因为在旋转后重新创建视图时它不存在。如果马特不介意,我可以编辑答案并提供代码片段。
嗯,对我来说,地图片段没有被删除。我一定是做错了什么,但是如果我调用 getActivity().getSupportFragmentManager().getFragments(),片段在调用 remove(f).commit 之后仍然存在。有谁知道为什么? (我用 getSupportFragManager 替换了 getFragManager)
commitAllowingStateLoss 将避免 IllegalStateException 异常。
Z
Zou

这是我的回答:

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

mMapFragment.getMap(); 返回 null。知道为什么吗?
@AnasAzeem,以编程方式创建片段可以完全解决问题,在您的情况下,您不必为解决方案获取 mMap。
@AnasAzeem,您应该使用 get getMapAsync ,它将在初始化后(在后台)正确返回地图实例。这是使用谷歌地图的“正确”方式,与片段无关
D
Desmond Lua

正如@Justin Breitfeller 所提到的,@Vidar Wahlberg 解决方案是一种可能无法在未来版本的 Android 中运行的 hack。 @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,您的解决方案对我来说完美无缺。您唯一需要记住的是不要在布局中创建地图。此解决方案中的地图创建嵌入在代码中,因此将 更改为例如
D
Digvijay Machale

全局声明 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"
    />

上面的代码解决了我的问题,它工作正常


C
CommonsWare

我会在标签处理中推荐 replace() 而不是 attach()/detach()

或者,切换到 ViewPagerHere is a sample project 显示带有标签的 ViewPager,托管 10 个地图。


但是如果我们使用分离窗口而不是地图问题发生,则替换给出 outofmemry 错误。
t
tomrozb

另一种解决方案:

if (view == null) {
    view = inflater.inflate(R.layout.nearbyplaces, container, false);
}

就是这样,如果不为空,则不需要重新初始化它从父级中删除是不必要的步骤。


这是我的情况的最佳解决方案。由于我的导航图使用了两个片段,我得到了这个错误,这解决了我的问题。
这是我的情况的最佳解决方案。由于我的导航图使用了两个片段,我得到了这个错误,这解决了我的问题。
M
Mahdi Giveie

您要返回或膨胀布局两次,只需检查您是否只膨胀一次。


伟大的捕捞!我会找几个小时,但没有注意到这一点。谢谢@MahdiGiveie
c
comeGetSome

我今天花了几个小时来寻找原因,幸运的是这个问题不是因为 MapFragment 实现,幸运的是,这不起作用,因为嵌套片段仅通过 rev 11 的支持库支持。

我的实现有一个带有操作栏的活动(在选项卡模式下),有两个选项卡(没有查看器),一个有地图,另一个有一个条目列表。当然,在我的选项卡片段中使用 MapFragment 时我非常天真,等等,每次我切换回地图选项卡时,应用程序都会崩溃。

(如果我的标签片段会膨胀包含任何其他片段的任何布局,我也会遇到同样的问题)。

一种选择是使用 MapView(而不是 MapFragment),尽管有一些开销(请参阅 MapView Docs 作为 layout.xml 中的直接替换,另一种选择是使用 rev. 11 的支持库,然后采用程序化方法,因为嵌套片段既不支持通过布局。或者只是通过显式销毁片段以编程方式解决(如在 Matt / Vidar 的答案中),顺便说一句:使用 MapView 实现相同的效果(选项 1)。

但实际上,我不想在每次 tab 离开时都松开地图,也就是说,我想将它保存在内存中并仅在 Activity 关闭时进行清理,所以我决定在 tab 时简单地隐藏/显示地图,参见 FragmentTransaction / hide


D
Daniel Nugent

对于那些仍然遇到此问题的人,确保您不会在 Tab 中的 Map 中出现此错误的最佳方法是使 Fragment 扩展 SupportMapFragment 而不是在用于的 Fragment 内嵌套 SupportMapFragment选项卡。

我刚刚使用带有 FragmentPagerAdapterViewPager 和第三个选项卡中的 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)));

            }
        });

    }


}

结果:

https://i.stack.imgur.com/Zx7au.png

这是我用来测试的完整类代码,其中包括用于前两个选项卡的占位符片段和用于第三个选项卡的地图片段:

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");

                }
            });

        }
    }
}

J
Jayant Arora

我尊重所有的答案,但我找到了一个线性解决方案:如果 n 是选项卡数,那么:

 mViewPager.setOffscreenPageLimit(n);

示例:如果提到:

 mViewPager.setOffscreenPageLimit(2);

View pager 实现了一个队列,因此您不必让它删除该片段。 onCreateView 只被调用一次。


R
Ratul Bin Tazul

我参加聚会有点晚了,但这些答案都没有帮助我。我在我的片段中都使用 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;
    }


}

I
Ivan

当前不支持嵌套片段。试试 Support Package, revision 11


J
JJD

您是否尝试在布局文件中引用您的自定义 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>

您能否发布自定义地图片段 com.nfc.demo.MapFragment 的代码
我不是代码的作者 - 我只是使用了有问题的代码。你得问问赫尔曼。
V
Vlad Hudnitsky

如果您只使用 Vidar Wahlberg 答案,则在打开其他活动(例如)并返回地图时会出现错误。或者在我的情况下打开其他活动,然后从新活动再次打开地图(不使用后退按钮)。但是,当您结合 Vidar Wahlberg 解决方案和 Matt 解决方案时,您将不会有例外。

布局

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

u
user1396018

我在 viewPager 中有这个,崩溃是因为任何片段都必须有自己的标签,不允许相同片段的重复标签或 ID。


m
maddy d

我认为以前的 App-Compat 库中存在一些针对子片段的错误。我尝试了@Vidar Wahlberg 和@Matt 的答案,它们对我不起作用。更新 appcompat 库后,我的代码无需任何额外努力即可完美运行。


H
Hitesh Sahu

这里需要注意的是,您的应用程序将在以下两种情况下严重崩溃:-

1)为了再次使用 Maps 片段重用 MapView 片段,当您的显示地图的片段在 onDestroyView 回调中被其他片段替换时,必须删除 MapView 片段。否则,当您尝试使用 com.google.android.gms.maps.MapFragment 的另一个片段重复 ID、标记 null 或父 ID 时,会发生两次膨胀相同的片段。 2)其次,您不能将 app.Fragment 操作与 android.support.v4.app.Fragment API 操作混合使用,例如,不要使用 android.app.FragmentTransaction 来删除 v4.app.Fragment 类型的 MapView Fragment。混合这将再次导致片段方面的崩溃。

这是正确使用 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"
       />

https://i.stack.imgur.com/hEnfG.jpg

希望它会帮助某人。


L
Lalit kumar

在此解决方案中,您不需要使用静态变量;

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

}`

h
hieudev develo

尝试为您的 mapView 父布局设置一个 id (android:id="@+id/maps_dialog")。为我工作。


b
barnacle.m

现在来这里的任何人在使用 Google 地方信息 AutocompleteSupportFragment 打开 Dialog 或其他 Fragment 时遇到此类错误,试试这个单行(我不知道这有多安全,但它对我有用) :

autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();

在您解雇/销毁您的片段之前。


A
Ankur Gautam
<?xml version="1.0" encoding="utf-8"?>

  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
   android:layout_height="match_parent" >


<com.google.android.gms.maps.MapView
    android:id="@+id/mapview"
    android:layout_width="100dip"
    android:layout_height="100dip"
    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 以前的版本

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅