티스토리 뷰
Fragment에서 inflate 중 "Duplicate ID, tag null, or parent id with another fragment" 오류 수정하기
맨날치킨 2023. 1. 25. 16:05Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.
Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.
아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.
Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment
com.google.android.gms.maps.MapFragment에 대해 중복된 ID, null 태그 또는 다른 프래그먼트와의 부모 ID가 있습니다.
문제 내용
I have an application with three tabs.
저는 세 개의 탭을 가진 애플리케이션이 있습니다.
Each tab has its own layout .xml file. The main.xml has its own map fragment. It's the one that shows up when the application first launches.
각각의 탭에는 자신만의 레이아웃 .xml 파일이 있습니다. main.xml에는 자체적인 맵 프래그먼트가 있습니다. 이것은 애플리케이션이 처음 실행될 때 나타납니다.
Everything works fine except for when I change between tabs. If I try to switch back to the map fragment tab, I get this error. Switching to and between other tabs works just fine.
이것은 애플리케이션이 처음 시작될 때 나타납니다. 다른 탭으로 전환하는 것은 문제가 없지만, 맵 프래그먼트 탭으로 다시 전환하려고하면이 오류가 발생합니다.
What could be wrong here?
여기서 무엇이 잘못될 수 있을까요?
This is my main class and my main.xml, as well as a relevant class that I use ( you will also find the error log at the bottom )
이것은 내 Main 클래스와 main.xml, 그리고 내가 사용하는 관련 클래스 (아래에서 오류 로그를 찾을 수 있습니다)입니다.
main class
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>
relevant class ( MapFragment.java )
적절한 클래스(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();
}
}
error
오류
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
높은 점수를 받은 Solution
The answer Matt suggests works, but it causes the map to be recreated and redrawn, which isn't always desirable. After lots of trial and error, I found a solution that works for me:
Matt가 제안한 답변은 작동하지만, 지도가 재생성되고 다시 그려지기 때문에 항상 바람직하지는 않습니다. 많은 시행 착오 끝에, 나에게 작동하는 해결책을 찾았습니다.
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;
}
For good measure, here's "map.xml" (R.layout.map) with R.id.mapFragment (android:id="@+id/mapFragment"):
참고로, "map.xml"이라는 레이아웃 파일에는 "R.id.mapFragment"이라는 아이디가 있습니다 (android:id="@+id/mapFragment").
<?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>
I hope this helps, but I can't guarantee that it doesn't have any adverse effects.
이것이 도움이 되기를 바라며, 이것이 부작용이 없는지 보장할 수는 없습니다.
Edit: There were some adverse effects, such as when exiting the application and starting it again. Since the application isn't necessarily completely shut down (but just put to sleep in the background), the previous code i submitted would fail upon restarting the application. I've updated the code to something that works for me, both going in & out of the map and exiting and restarting the application, I'm not too happy with the try-catch bit, but it seem to work well enough. When looking at the stack trace it occurred to me that I could just check if the map fragment is in the FragmentManager, no need for the try-catch block, code updated.
수정: 일부 부작용이 있었는데, 예를 들어 애플리케이션을 종료하고 다시 시작할 때입니다. 애플리케이션이 완전히 종료되지는 않고 (배경에서 대기 중), 이전에 제출한 코드는 애플리케이션을 다시 시작할 때 실패할 것입니다. 나는 내가 잘 작동하는 코드로 업데이트했는데, try-catch 부분은 그다지 좋아하지 않지만, 충분히 잘 작동하는 것 같다. 스택 추적을 보면 MapFragment가 FragmentManager에 있는지만 확인하면 try-catch 블록이 필요하지 않다는 것이 떠올랐다. 코드가 업데이트되었다.
More edits: It turns out that you need that try-catch after all. Just checking for the map fragment turned out not to work so well after all. Blergh.
추가 수정사항: 결국 try-catch가 필요하다는 것을 알게 되었습니다. 맵 프래그먼트를 확인하는 것만으로는 잘 작동하지 않았습니다. 아쉽습니다.
가장 최근 달린 Solution
You are returning or inflating layout twice, just check to see if you only inflate once.
레이아웃을 두 번 반환하거나 인플레이트하고 있습니다. 인플레이트는 한 번만 하는지 확인하세요.
출처 : https://stackoverflow.com/questions/14083950/duplicate-id-tag-null-or-parent-id-with-another-fragment-for-com-google-androi
'개발 > 안드로이드' 카테고리의 다른 글
안드로이드 스튜디오에서 디버깅을 하려고 할 때 "Waiting For Debugger"에서 멈춰있는 경우 해결 방법 (0) | 2023.01.28 |
---|---|
외부 디렉토리에 있는 이미지 파일 이미지 뷰로 불러오기 (0) | 2023.01.26 |
Gradle에서 transitive = true의 동작 방식 (0) | 2023.01.24 |
텍스트뷰 왼쪽 드로어블을 프로그래밍 방식으로 설정하기 (0) | 2023.01.23 |
RecyclerView에서 WRAP_CONTENT 동작 오류 수정하기 (0) | 2023.01.23 |