【问题标题】:Using SupportMapFrament in a two pane layout gives duplicate layout error在两个窗格布局中使用 SupportMapFrament 会产生重复的布局错误
【发布时间】:2013-01-24 10:25:12
【问题描述】:

我正在使用一个驱动细节视图的 ListView。列表视图和详细信息视图有自己的活动。详细视图以较小的布局显示地图。地图片段代码如下:

<RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="150dp" android:id="@+id/details_emap_container"
        android:background="@drawable/back_rounded_rectangle_border_black"
        android:padding="2dp"
        >
    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
              android:tag="details_map_short"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              class="com.google.android.gms.maps.SupportMapFragment"
              />
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/rightArrowMap" android:src="@drawable/right_arrow_black"
            android:layout_alignParentRight="true" android:layout_centerVertical="true"
            android:layout_marginRight="10dp"/>
</RelativeLayout>

当我在平板电脑上的两个窗格布局中第一次单击列表项时,它工作正常。但是在随后选择任何其他列表项时,会引发以下错误:

android.view.InflateException: Binary XML file line #159: Error inflating class fragment
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:688)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:724)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:727)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:727)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:727)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:479)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:391)
        at com.xxxx.android.activity.fragments.EventDetailFragment.onCreateView(EventDetailFragment.java:97)
        at android.support.v4.app.Fragment.performCreateView(Fragment.java:1460)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:911)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
        at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
        at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
        at android.os.Handler.handleCallback(Handler.java:587)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:132)
        at android.app.ActivityThread.main(ActivityThread.java:4123)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:491)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
        at dalvik.system.NativeStart.main(Native Method)
        Caused by: java.lang.IllegalArgumentException: Binary XML file line #159: Duplicate id 0xffffffff, tag details_map_short, or parent id 0x0 with another fragment for com.google.android.gms.maps.SupportMapFragment
        at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:285)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:660)
        ... 22 more

EventDetailFragment 如下所示:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    Log.e(TAG, "onCreateView was called!");
    // If activity recreated (such as from screen rotate), restore
    // the previous article selection set by onSaveInstanceState().
    // This is primarily necessary when in the two-pane layout.
    if (savedInstanceState != null) {
        mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
        mCurrEventId = savedInstanceState.getString(EVENT_ID);
    }

    // Inflate the layout for this fragment
    detailsView = inflater.inflate(R.layout.fragment_event_detail, container, false);

    mMapFragment = (SupportMapFragment)getFragmentManager().findFragmentByTag(MAP_FRAGMENT_TAG);

    // We can't be guaranteed that the map is available because Google Play services might
    // not be available.
    //setUpMapIfNeeded();  //do this later in onStart

    return detailsView;

}

列表活动中的代码,用于在选择项目时显示详细活动或片段:

@Override
public void onItemSelected(int index) {
    Log.d(TAG, "Item Selected is : " + index);
    if(findViewById(R.id.event_details_container) != null) {
        // In two-pane mode, show the detail view in this activity by
        // adding or replacing the detail fragment using a
        // fragment transaction.
        Log.d(TAG, "Two pane layout navigation executed");
        Bundle arguments = new Bundle();
        arguments.putInt(EventDetailFragment.ARG_POSITION, index);
        EventDetailFragment fragment = new EventDetailFragment();
        fragment.setArguments(arguments);
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.event_details_container, fragment).commit();
    }
    else {
        // In single-pane mode, simply start the detail activity
        // for the selected item ID.
        Log.d(TAG, "Single pane layout navigation - creating new Activity to display!");
        getSupportFragmentManager().executePendingTransactions();
        Intent detailIntent = new Intent(this, EventDetailActivity.class);
        detailIntent.putExtra(EventDetailFragment.ARG_POSITION, index);
        startActivity(detailIntent);
    }

}

我无法弄清楚为什么这在两个窗格布局中不起作用?为什么只有 MapFragment 似乎会抛出错误?在我添加 SupportMapFragment 之前,这一切都很好。任何帮助/想法将不胜感激!

PS:代码必须支持 API 级别 10 及以上。

【问题讨论】:

  • 只是想补充一下,由于我没有平板设备,因此正在模拟器上进行测试。第一次地图片段显示在详细视图中,它说更新谷歌服务。这可能是错误的原因吗?这在实际设备上可以正常工作吗?

标签: android android-fragments android-lifecycle google-maps-android-api-2 android-maps-v2


【解决方案1】:

我终于找到了正确的解决方法。由于我在两个窗格布局中的片段中使用地图;我必须以编程方式构建地图,而不是在 xml 中定义。这在 android SDK (googleplayservices) 中的地图示例 ProgrammaticDemoActivity 中得到了演示。下面是示例的摘录,当从 Fragment 调用时从 onCreateView 调用(如果从 Activity 调用,则从 onCreate 调用):

// It isn't possible to set a fragment's id programmatically so we set a tag instead and
// search for it using that.
mMapFragment = (SupportMapFragment) getSupportFragmentManager()
        .findFragmentByTag(MAP_FRAGMENT_TAG);

// We only create a fragment if it doesn't already exist.
if (mMapFragment == null) {
    // To programmatically add the map, we first create a SupportMapFragment.
    mMapFragment = SupportMapFragment.newInstance();

    // Then we add it using a FragmentTransaction.
    FragmentTransaction fragmentTransaction = getSupportFragmentManager()
            .beginTransaction();
    fragmentTransaction.add(android.R.id.content, mMapFragment, MAP_FRAGMENT_TAG);
    fragmentTransaction.commit();
}

之所以如此,是因为默认情况下,地图片段将拥有自己的生命周期,而不是与视图生命周期相关联。因此,如果您在 xml 中定义它,则地图片段不会被破坏,但视图会被破坏。当您尝试再次为片段充气时,这会导致错误,因为地图已经存在!有关更多信息,请参阅:https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/MapFragment

【讨论】:

  • 您应该从 getSupportFragmentManger 更改为 getChildFragmentManager。除此之外,这就是我们应该如何处理嵌套片段。
【解决方案2】:

Google Maps for Android API v2 目前无法在开箱即用的模拟器上运行。 可能需要设置一些 YMMV,请参阅 - This app won't run unless you update Google Play Services (via Bazaar)。您最好的选择是在真实设备上进行测试,直到 Google 解决了问题。请给错误加注星标 - http://code.google.com/p/gmaps-api-issues/issues/detail?id=4627

【讨论】:

  • 我知道地图无法在模拟器上运行,我对此很好。但是我不明白上述错误的原因。顺便说一句,我能够在平板设备上进行测试,它似乎给出了与上述相同的错误。这仅适用于平板设备(具有两个窗格布局),不适用于单窗格布局(在模拟器或实际设备上)。
  • 不应该在onCreateView()mMapFragment = (SupportMapFragment)getFragmentManager().findFragmentByTag(MAP_FRAGMENT_TAG); 行中getFragmentManager() 成为getSupportFragmentManager()?还要检查您是否两次使用相同的MAP_FRAGMENT_TAG
  • 不,包含mapFragmentDetailsFragmentFragment 本身而不是FragmentActivity
猜你喜欢
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 2013-11-23
  • 2011-06-15
  • 2013-01-06
  • 2012-02-17
  • 2012-07-15
  • 1970-01-01
相关资源
最近更新 更多