【问题标题】:MapView inside Fragment - specified child already has a parent片段内的 MapView - 指定的孩子已经有一个父母
【发布时间】:2012-03-15 11:38:18
【问题描述】:

我正在尝试在片段中显示 MapView(使用被黑的兼容性库)。以下在过去工作得很好:

  • 片段的onCreateView() 只返回一个新的FrameLayout
  • fragment 的 onActivityCreated() 从 Acitivity 中获取 MapView 并将其添加到其视图层次结构中
  • onDestroyView() 将 MapView 从其视图层次结构中移除

现在我希望片段使用在 xml 中定义的布局,以便我可以拥有一些其他 UI 内容。将MapView元素放在布局文件中总是会崩溃,所以我是这样做的:

ma​​p_screen_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <FrameLayout
        android:id="@+id/map_container"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </FrameLayout>

</LinearLayout>

我的MapScreenActivity 拥有实际的MapView,片段调用getMapView(),所以我不会遇到“不能有多个 MapView”的问题:

MapScreenActivity.java

public class MapScreenActivity extends FragmentActivity {
    protected Fragment fragment;
    protected MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.single_pane_empty);

        if (savedInstanceState == null) {
            fragment = new MapScreenFragment();

            getSupportFragmentManager().beginTransaction().add(R.id.root_container, fragment)
                    .commit();
        }
    }

    public MapView getMapView() {
        if (mapView == null) {
            mapView = new MapView(this, getResources().getString(R.string.maps_api_key));
        }

        return mapView;
    }
}

MapScreenFragment.java

public class MapScreenFragment extends Fragment {
    protected ViewGroup mapContainer;
    protected MapView mapView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle args) {
        View root = inflater.inflate(R.layout.map_screen_fragment, container);
        mapContainer = (ViewGroup) root.findViewById(R.id.map_container);
        return root;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mapView = ((MapScreenActivity) getActivity()).getMapView();
        mapView.setClickable(true);
        mapView.setBuiltInZoomControls(true);

        mapContainer.addView(mapView);

    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mapContainer.removeView(mapView);
    }   

}

理论上,这应该与第一次描述的new FrameLayout 方法相同。但是,我每次都得到这个:

02-24 18:01:28.139: E/AndroidRuntime(502): FATAL EXCEPTION: main
02-24 18:01:28.139: E/AndroidRuntime(502): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.mapfragment/com.example.mapfragment.MapScreenActivity}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.ActivityThread.access$1500(ActivityThread.java:117)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.os.Handler.dispatchMessage(Handler.java:99)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.os.Looper.loop(Looper.java:130)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.ActivityThread.main(ActivityThread.java:3683)
02-24 18:01:28.139: E/AndroidRuntime(502):  at java.lang.reflect.Method.invokeNative(Native Method)
02-24 18:01:28.139: E/AndroidRuntime(502):  at java.lang.reflect.Method.invoke(Method.java:507)
02-24 18:01:28.139: E/AndroidRuntime(502):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
02-24 18:01:28.139: E/AndroidRuntime(502):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
02-24 18:01:28.139: E/AndroidRuntime(502):  at dalvik.system.NativeStart.main(Native Method)
02-24 18:01:28.139: E/AndroidRuntime(502): Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.view.ViewGroup.addViewInner(ViewGroup.java:1976)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.view.ViewGroup.addView(ViewGroup.java:1871)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.view.ViewGroup.addView(ViewGroup.java:1828)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.view.ViewGroup.addView(ViewGroup.java:1808)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.support.v4.app.NoSaveStateFrameLayout.wrap(Unknown Source)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.support.v4.app.FragmentManagerImpl.moveToState(Unknown Source)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.support.v4.app.FragmentManagerImpl.moveToState(Unknown Source)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.support.v4.app.BackStackRecord.run(Unknown Source)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.support.v4.app.FragmentManagerImpl.execPendingActions(Unknown Source)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.support.v4.app.FragmentActivity.onStart(Unknown Source)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.Activity.performStart(Activity.java:3791)
02-24 18:01:28.139: E/AndroidRuntime(502):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1620)
02-24 18:01:28.139: E/AndroidRuntime(502):  ... 11 more

在从getMapView() 返回之前,我尝试从其父级中删除MapView,但仍然崩溃。我真的不明白为什么这种方法不起作用,任何帮助都将不胜感激。

【问题讨论】:

    标签: android android-mapview fragment


    【解决方案1】:

    我不确定这是否与您遇到的问题相同,但事实证明充气是可以的,只要您不附加到根即可。

    例如,您需要执行以下操作:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.id.my_layout, container, false);
    }
    

    如果您未能将最后一个 false 参数添加到 inflate() 调用,那么您将收到 IllegalStateException。

    我认为正在发生的事情是,如果没有额外的 false 参数,您的膨胀视图树会附加到根视图 (container),然后当 Activity 启动时,系统会尝试将视图树添加到又是根。因此出现错误。

    【讨论】:

    • 我重新审视那个项目已经有一段时间了,但当时的片段对我来说还很新鲜,所以我怀疑这实际上是正确的答案。
    • 谢谢你,直到你展示了最后一个参数才意识到发生了什么:)
    【解决方案2】:

    在尝试了很多事情之后,我最终做到了:

    我在onCreateView() 中返回的根视图是以编程方式创建的,而不是膨胀的。但是,膨胀其他视图并将它们作为子视图添加到以编程方式创建的根视图中似乎不会导致任何问题。

    向任何能够弄清楚这种奇怪行为背后的原因的人致敬。希望这对其他人有用。

    编辑

    我已经有一段时间没有重新讨论这个话题了,但我记得,这不会对我有用...

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) {
        return inflater.inflate(R.layout.some_layout, container, false);
    }
    

    ...而这对我有用...

    private ViewGroup mapContainer;
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) {
        mapContainer = new LinearLayout(getActivity());
        return mapContainer;
    }
    

    ... 稍后在onActivityCreated() 中,我将从活动中获得一个 MapView 并将其添加为 mapContainer 的子项。如果我想要其他视图(比如MapView 上方的标题),我可以单独对它们进行膨胀并将它们添加到 mapContainer,如这段代码 sn-p 所示。

    private ViewGroup mapContainer;
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) {
        mapContainer = new LinearLayout(getActivity());
        mapContainer.setOrientation(LinearLayout.VERTICAL);
        View headerView = inflater.inflate(R.layout.some_layout, mapContainer, false);
        mapContainer.addView(headerView);
        return mapContainer;
    }
    

    【讨论】:

    • 你能发一个例子吗?
    【解决方案3】:

    这对我有用... 每当我切换片段时,我都会这样做

    if (mapContainer.getChildAt(0) != null){
            mapContainer.removeViewAt(0);
    }
    

    我没有把它放在我的 onDestroy() 或 onPause() 方法中。每当我切换片段时,我都会这样做。由于某种原因,我的片段没有调用 onPause()。

    在我的 onResume() 方法中我做了

    mapContainer.addView(mapView);
    

    一切都适合我。不再有 IllegalStateExceptions。

    【讨论】:

      【解决方案4】:

      [更新] 我写了一个小库,将所有这些 LocalActivityManager 解决方案混合在一起:https://github.com/coreform/android-tandemactivities

      [旧帖] 你应该

      mapContainer.removeView(mapView);
      

      在你之前

      mapContainer.addView(mapView);
      

      为了避免这样的异常。

      ...就像之前的那一行一样。

      好的,在cmets之后跟进:

      试试这个:

      if(mapView.getParent() != null) {
          mapContainer.addView(mapView);
      }
      

      【讨论】:

      • 我也试过了,还是不行。仍然给出完全相同的错误。
      • 我也尝试过((ViewGroup) mapView.getParent()).removeView(mapView) 的变体,但都没有。
      • 尝试找出您的 MapView 已经附加到哪个父级。如果它是您打算将其添加到的父级,则添加它,以便您可以使用逻辑或 try/catch 语句处理该案例。我使用了我在这个线程上的 cmets 中发布的解决方案:nextlevelandroid.com/?p=114 (cmets by lindo) 它可以工作并且不需要每个 friggen FragmentActivity 来扩展 MapActivity!
      • @straya :请您分享您的代码以便于理解。我浏览了很多帖子,但找不到简单的解决方案。
      • 嘿,我会尝试整理一个简洁、通用的示例……但现在我正忙于参加 google IO! XD
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-21
      • 2021-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多