태그 보관물: android-fragments

android-fragments

com.google.android.gms.maps.MapFragment의 다른 조각과 중복 된 ID, 태그 null 또는 부모 ID

세 개의 탭이있는 응용 프로그램이 있습니다.

각 탭에는 자체 레이아웃 .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();
        }
    }

}

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


답변

매트가 답을 제시하지만,지도를 다시 작성하고 다시 그려야하므로 항상 바람직하지는 않습니다. 많은 시행 착오 끝에 나에게 맞는 해결책을 찾았습니다.

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가 필요하다는 것이 밝혀졌습니다. 지도 조각을 검사하는 것만으로도 제대로 작동하지 않는 것으로 나타났습니다. 블러그.


답변

문제는 당신이하려는 일을해서는 안된다는 것입니다. 다른 조각 안에 조각을 부풀려서는 안됩니다. 안드로이드 문서에서 :

참고 : 레이아웃에 <fragment>가 포함되어 있으면 레이아웃을 조각으로 팽창시킬 수 없습니다. 중첩 된 조각은 조각에 동적으로 추가 된 경우에만 지원됩니다.

여기에 제시된 해킹으로 작업을 수행 할 수는 있지만 그렇게하지 않는 것이 좋습니다. 이러한 해킹이 다른 조각을 포함하는 조각의 레이아웃을 부풀 리려고 할 때 각각의 새로운 Android OS가 수행하는 작업을 처리하는지 확인하는 것은 불가능합니다.

다른 프래그먼트에 프래그먼트를 추가하는 유일한 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>

그런 다음 조각 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);
}

답변

나는 같은 문제가 있었고 클래스 MapFragmentonDestroy()메소드 에서 수동으로를 제거하여 해결할 수있었습니다 Fragment. 다음은 MapFragmentXML에서 by ID를 사용 하고 참조하는 코드입니다 .

@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를 가진 MapView각 새 항목에 복제본 이 만들어집니다 MapFragment. 해결책은 수동으로MapFragment 조각을 하여 조각이 팽창 될 때마다 기본 맵을 다시 작성하는 것입니다.

나는 또 다른 답변에서 이것을 언급했다 [ 1 ].


답변

이것은 내 대답입니다.

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 클래스에서 프로그래밍 방식으로 Google 맵을 추가하십시오.

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

답변

  1. @Justin Breitfeller가 언급했듯이 @Vidar Wahlberg 솔루션은 향후 버전의 Android에서는 작동하지 않을 수 있습니다.
  2. @Vidar Wahlberg는 다른 솔루션이 “맵을 다시 작성하고 다시 그려야하므로 항상 바람직하지는 않기 때문에”해킹을 유발합니다. 매번 새 인스턴스를 만드는 대신 이전 맵 조각을 유지하면 맵 다시 그리기를 방지 할 수 있습니다.
  3. @Matt 솔루션이 작동하지 않습니다 (IllegalStateException)
  4. @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;
}

답변

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

위의 코드는 내 문제를 해결했으며 정상적으로 작동합니다.


답변

내가 추천 할 것입니다 replace()보다는 attach()/detach()탭 처리에서 합니다.

또는로 전환하십시오 ViewPager. 다음은 샘플 프로젝트 을 보여주는 ViewPager(10 개)지도를 호스팅 탭은.