본문 바로가기

안드로이드 개발 팁/일반

다수의 프래그먼트 및 구글맵 프래그먼트를 사용할 때 자주 발생하는 오류 대처법

프래그먼트를 사용하다 보면 생각치도 못한 곳에서 오류가 자주 발생하곤 합니다. 이 포스트에서는 프래그먼트를 사용하다 보면 자주 접할 수 있는 오류 두 가지를 소개하고, 각 오류에 대한 해결 방법에 대해 다룹니다.


1. duplicate id tag null or parent id with another fragment for com.google.android.gms.maps.mapfragment


구글맵을 사용하다 보면 빈번하게 접하는 오류입니다. 분명 잘못된 것이 없어 보이는데, 위와 같은 오류가 뜨면 참 난감하지요. 에러 로그는 일반적으로 다음과 유사한 형태입니다.


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


이와 유사한 형태의 다른 에러를 하나 더 봅시다.


2. the specified child already has a parent. you must call removeview() on the child's parent first


이 에러도 프래그먼트를 사용하다 보면 접하기 쉬운데, 주로 여러 프래그먼트를 전환하는 구성을 사용할 때 발생하기 쉽습니다. (FragmentTransaction.replace(), add(), remove() 등을 사용하여 한 액티비티 내에서 선택한 메뉴에 따라 다른 프래그먼트를 보여주는 경우가 대표적인 예)


위에서 본 두 사례의 원인은 모두 '프래그먼트를 구성하는 뷰(View)를 중복해서 레이아웃 내에 추가하려 했기 때문' 입니다. 프래그먼트가 화면에 표시되는 과정에서, 프래그먼트가 표시되는 뷰(컨테이너 뷰)에 프래그먼트의 뷰가 추가되는데, 이 상테에서 다시 프래그먼트를 추가하면 뷰가 중복되어 오류가 발생하는 것입니다.


이를 방지하려면, 프래그먼트가 화면에서 사라질 때 프래그먼트의 뷰를 컨테이너 뷰에서 제거해주면 됩니다. 일반적으로 다음과 프래그먼트의 코드 내에 onDestroyView()를 다음과 같이 오버라이드 하면 됩니다. 프래그먼트가 화면에서 사라질 때, 컨테이너 뷰(parent)에서 프래그먼트 뷰(v)를 제거하여 이후에 중복 추가되는 것을 방지하는 원리입니다.

static View v; // 프래그먼트의 뷰 인스턴스
@Override
public void onDestroyView() {
    super.onDestroyView();
    if(v!=null){
        ViewGroup parent = (ViewGroup)v.getParent();
        if(parent!=null){
            parent.removeView(v);
        }
    }
}

간혹, 구글맵(MapFragment, SupportMapFragment)을 사용하는 프래그먼트의 경우 위 코드를 적용해도 inflate시 에러가 발생하는 경우가 있습니다. 이를 방지하려면 아래 코드를 onCreateView()에 추가하면 됩니다.

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
        try{
            v = inflater.inflate(R.layout.fragment_nearby, container, false);
        }catch (InflateException e){
            // 구글맵 View가 이미 inflate되어 있는 상태이므로, 에러를 무시합니다.
        }
        // 이후 메서드 구현 계속
}