#android #android-fragments #fragmentmanager
#Android #android-фрагменты #fragmentmanager
Вопрос:
У меня есть фрагмент, xml которого содержит что-то вроде этого:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="230dp">
<fragment
android:id="@ id/myFragmen1"
android:name="com...widgets.myViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text""/>
</RelativeLayout>
Мне нужно добавить 4 разных фрагмента в мой xml. Родительский фрагмент я начинаю так:
getFragmentManager().beginTransaction()
.replace(R.id.frameContainer, myFragment1)
.addToBackStack(null)
.commit();
Итак, все работает нормально, пока я не попытаюсь показать фрагмент более 1 раза. Если я нажму назад и попытаюсь снова открыть фрагмент, то я получаю эти ошибки:
Caused by: java.lang.IllegalArgumentException: Binary XML file line #17: Duplicate id 0x7f080130, tag null, or parent id 0xffffffff with another fragment for com...widgets.myViewPager
Я знаю проблему, а также у меня есть решение. Я мог бы удалить фрагмент в onDestroy. Но я не хочу этого делать!
XML, который содержит другой фрагмент, выполняется динамически. Я получаю макет через пакет, поэтому в этом фрагменте я не могу знать, какой фрагмент показан. И если я использую эту часть более 1 раза, я должен быть уверен, что имя совпадает. Итак, мой вопрос: есть ли возможность удалить в каждом фрагменте? Так что каждый фрагмент удаляет себя, а родительскому нечего делать?
Спасибо за помощь
Редактировать:
MyFragment myFragment1 = new MyFragment();
Bundle bundle = new Bundle();
bundle.putInt("layout",R.layout.my_fragment);
myFragment1.setArguments(bundle);
getFragmentManager().beginTransaction()
.replace(R.id.frameContainer, myFragment1)
.addToBackStack(null)
.commit();
Эти вызовы я делаю из разных мест. Для каждого вызова я могу создать собственный xml с разными фрагментами.
И в моем родительском фрагменте я делаю это:
int layoutId = this.getArguments().getInt("layout", 0);
if(layoutId==0)
throw new RuntimeException("LayoutId not found");
return inflater.inflate(layoutId,container,false);
После этого мой фрагмент загружает xml со всеми фрагментами, которые я установил в xml. Итак, я могу определить вид извне. После удаления из этого фрагмента и повторного открытия он выдаст ошибку. Это потому, что все фрагменты в моем xml не уничтожаются! Мой родительский фрагмент не знает, какие фрагменты находятся в xml, поэтому я хочу, чтобы они уничтожили себя в своем классе.
Комментарии:
1. чего вы достигли с этим?
Ответ №1:
Эта ошибка возникает из-за того, что дочерние Fragment
элементы могут быть созданы и отображены только программно. XML-макет a Fragment
не может содержать <fragment ... />
тег. Из документации:
You cannot inflate a layout into a fragment when that layout includes a <fragment>. Nested fragments are only supported when added to a fragment dynamically.
Вот что вам нужно сделать:
ШАГ 1: Измените Fragment
родительский XML-макет следующим образом:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="230dp">
<!-- <fragment
android:id="@ id/myFragmen1"
android:name="com...widgets.myViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" /> -->
<FrameLayout
android:id="@ android:id/childfragmentregion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="0dp"
android:layout_weight="1" >
</FrameLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text""/>
</RelativeLayout>
ШАГ 2: Затем для замены новых дочерних фрагментов:
FragmentManager fm = getChildFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.childfragmentregion, new SomeXYZFragment(), "some_xyz_fragment");
ft.commit();
ШАГ 3: переопределите Fragment
родительский onDetach()
метод следующим образом:
@Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Это будет работать без исключений. Я прошу вас прояснить вторую часть вашего вопроса: мне непонятно, что Fragment
получает данные в a Bundle
. Я могу помочь вам дальше, если вы сделаете это более понятным.
Комментарии:
1. Спасибо за помощь, но я думаю, это не поможет. Может быть, мне следует объяснить это немного лучше: у меня есть 4 фрагмента, которые содержат только простой контент, например, 2 Textviews… Следующий комментарий слишком длинный для одного комментария 🙂
2. Теперь мне просто нужно создать xml, который содержит только те фрагменты, которые мне нужны. Я делаю это так, потому что у меня есть несколько разных мест, откуда я буду показывать фрагмент, но с другим контентом. Поэтому я просто создаю xml с фрагментами и помещаю эти xml в пакет. При запуске фрагмент загружает xml с различными фрагментами. Таким образом, я могу обрабатывать содержимое представления извне, но мне не нужно заботиться о данных, которые должны отображаться.
3. Я просто указал причину ошибки, которую я считаю правильной. И я не совсем понимаю, что вы подразумеваете под «помещением xml в пакет». Я так понимаю, вы имеете в виду, что вы передаете строковое значение
TextView
s в дочернемFragment
s вBundle
s.4. Для каждого места, откуда я хочу вызвать фрагмент, я создаю xml, который содержит мои фрагменты (например, xml в моем вопросе). Затем я помещаю layoutId xml в пакет и затем раздуваю эти xml. Я редактирую свой вопрос, надеюсь, это поможет вам понять