Мне нужна помощь в работе с фрагментом внутри фрагмента, на самом деле я столкнулся с проблемой при нажатии кнопки возврата. На главном экране приложения есть кнопки, и нажатие на каждую кнопку заменяет новый фрагмент (и этот фрагмент содержится внутри другого фрагмента), динамическое добавление / замена фрагмента работает нормально, при нажатии кнопки button1 фрагмент заменен, то же самое происходит при нажатии кнопки, но если я нажимаю кнопка снова, получилось исключение:
"Duplicate id 0x7f05000a, tag null, or parent id 0x7f050009 with
another fragment for com........ fragmentname"
означает, что фрагмент или внутренние фрагменты уже добавлены, и я пытаюсь добавить их снова, кто-нибудь знает, как работать с фрагментом внутри фрагмента и перемещаться вперед и назад без каких-либо проблем, спасибо за поддержку.
MainActivity, где фрагменты динамически добавляются и заменяются.
public class FragmentInsideFragmentTestActivity extends Activity {
private Button button1;
private Button button2;
private Button button3;
private Button button4;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1 =(Button) this.findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
button2 =(Button) this.findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
button3 =(Button) this.findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
button4 =(Button) this.findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
}
public void onButtonClick(View v) {
Fragment fg;
switch (v.getId()) {
case R.id.button1:
fg=FirstFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button2:
fg=SecondFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button3:
fg=FirstFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button4:
fg=SecondFragment.newInstance();
replaceFragment(fg);
break;
}
}
private void replaceFragment(Fragment newFragment) {
FragmentTransaction trasection = getFragmentManager().beginTransaction();
if(!newFragment.isAdded()) {
try {
//FragmentTransaction trasection =
getFragmentManager().beginTransaction();
trasection.replace(R.id.linearLayout2, newFragment);
trasection.addToBackStack(null);
trasection.commit();
} catch (Exception e) {
// TODO: handle exception
// AppConstants.printLog(e.getMessage());
} else {
trasection.show(newFragment);
}
}
}
Вот макет: main.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">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button1" />
<Button
android:id="@+id/button2"
android:text="Button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button3"
android:text="Button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button4"
android:text="Button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</LinearLayout>
Надеюсь, я попытался решить свою проблему.
источник
Ответы:
AFAIK, фрагменты не могут содержать другие фрагменты.
ОБНОВИТЬ
С текущими версиями пакета поддержки Android - или собственными фрагментами на уровне API 17 и выше - вы можете вкладывать фрагменты с помощью
getChildFragmentManager()
. Обратите внимание, что это означает, что вам необходимо использовать версию фрагментов из пакета поддержки Android на уровнях API 11–16, потому что, хотя на этих устройствах есть собственная версия фрагментов, в этой версии нетgetChildFragmentManager()
.источник
getChildFragmentManager()
и удалениеsetRetainInstance(true)
из него (жалко). Спасибо, что снова спас мой бекон, @CommonsWare.Мне нужно было больше контекста, поэтому я сделал пример, чтобы показать, как это делается. Самое полезное, что я прочитал во время подготовки, было следующее:
Деятельность
activity_main.xml
Добавьте
FrameLayout
к своей активности родительский фрагмент.<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Activity"/> <FrameLayout android:id="@+id/parent_fragment_container" android:layout_width="match_parent" android:layout_height="200dp"/> </LinearLayout>
MainActivity.java
Загрузите родительский фрагмент и реализуйте прослушиватели фрагментов. (См. Сообщение фрагмента .)
import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; public class MainActivity extends AppCompatActivity implements ParentFragment.OnFragmentInteractionListener, ChildFragment.OnFragmentInteractionListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Begin the transaction FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.parent_fragment_container, new ParentFragment()); ft.commit(); } @Override public void messageFromParentFragment(Uri uri) { Log.i("TAG", "received communication from parent fragment"); } @Override public void messageFromChildFragment(Uri uri) { Log.i("TAG", "received communication from child fragment"); } }
Родительский фрагмент
fragment_parent.xml
Добавьте еще один
FrameLayout
контейнер для дочернего фрагмента.<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="20dp" android:background="#91d0c2"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Parent fragment"/> <FrameLayout android:id="@+id/child_fragment_container" android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout> </LinearLayout>
ParentFragment.java
Используйте
getChildFragmentManager
inonViewCreated
для настройки дочернего фрагмента.import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; public class ParentFragment extends Fragment { private OnFragmentInteractionListener mListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_parent, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { Fragment childFragment = new ChildFragment(); FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); transaction.replace(R.id.child_fragment_container, childFragment).commit(); } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnFragmentInteractionListener) { mListener = (OnFragmentInteractionListener) context; } else { throw new RuntimeException(context.toString() + " must implement OnFragmentInteractionListener"); } } @Override public void onDetach() { super.onDetach(); mListener = null; } public interface OnFragmentInteractionListener { // TODO: Update argument type and name void messageFromParentFragment(Uri uri); } }
Дочерний фрагмент
fragment_child.xml
Здесь нет ничего особенного.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="20dp" android:background="#f1ff91"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Child fragment"/> </LinearLayout>
ChildFragment.java
Здесь тоже нет ничего особенного.
import android.support.v4.app.Fragment; public class ChildFragment extends Fragment { private OnFragmentInteractionListener mListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_child, container, false); } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnFragmentInteractionListener) { mListener = (OnFragmentInteractionListener) context; } else { throw new RuntimeException(context.toString() + " must implement OnFragmentInteractionListener"); } } @Override public void onDetach() { super.onDetach(); mListener = null; } public interface OnFragmentInteractionListener { // TODO: Update argument type and name void messageFromChildFragment(Uri uri); } }
Примечания
источник
Начиная с Android 4.2 (API 17) становятся доступны вложенные фрагменты http://developer.android.com/about/versions/android-4.2.html#NestedFragments
Чтобы разместить фрагмент внутри другого фрагмента, используйте getChildFragmentManager ()
Он также доступен в библиотеке поддержки!
источник
Фрагменты могут быть добавлены внутрь других фрагментов, но тогда вам нужно будет удалять его из родительского фрагмента каждый раз, когда
onDestroyView()
вызывается метод родительского фрагмента. И снова добавьте его вonCreateView()
метод родительского фрагмента .Просто сделай так:
@Override public void onDestroyView() { FragmentManager mFragmentMgr= getFragmentManager(); FragmentTransaction mTransaction = mFragmentMgr.beginTransaction(); Fragment childFragment =mFragmentMgr.findFragmentByTag("qa_fragment") mTransaction.remove(childFragment); mTransaction.commit(); super.onDestroyView(); }
источник
onActivityCreated()
него добавлены два фрагмента, по одному в каждое из представлений, используяgetFragmentManager()
. Это сработало в первый раз, но при вращении и возобновлении был виден только один из фрагментов.getChildFragmentManager()
Вместо этого поведение было правильным . Предложенный здесь подходonSaveInstanceState()
вызвал исключение, поскольку действие уже было вызвано. Фиксация транзакции с использованиемcommitAllowingStateLoss()
позволила избежать исключения, но не решила исходную проблему.Я решил эту проблему. Вы можете использовать библиотеку поддержки и
ViewPager
. Если вам не нужно смахивать жестами, вы можете отключить смахивание. Итак, вот код для улучшения моего решения:public class TestFragment extends Fragment{ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag, container, false); final ArrayList<Fragment> list = new ArrayList<Fragment>(); list.add(new TrFrag()); list.add(new TrFrag()); list.add(new TrFrag()); ViewPager pager = (ViewPager) v.findViewById(R.id.pager); pager.setAdapter(new FragmentPagerAdapter(getChildFragmentManager()) { @Override public Fragment getItem(int i) { return list.get(i); } @Override public int getCount() { return list.size(); } }); return v; } }
PS Это уродливый код для тестирования, но он улучшает то, что возможно.
Фрагмент PPS Inside
ChildFragmentManager
следует передать вViewPagerAdapter
источник
вы можете использовать
getChildFragmentManager()
функцию.пример:
Родительский фрагмент:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.parent_fragment, container, false); } //child fragment FragmentManager childFragMan = getChildFragmentManager(); FragmentTransaction childFragTrans = childFragMan.beginTransaction(); ChildFragment fragB = new ChildFragment (); childFragTrans.add(R.id.FRAGMENT_PLACEHOLDER, fragB); childFragTrans.addToBackStack("B"); childFragTrans.commit(); return rootView; }
Родительский макет (
parent_fragment.xml
):<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> <FrameLayout android:id="@+id/FRAGMENT_PLACEHOLDER" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
Дочерний фрагмент:
public class ChildFragment extends Fragment implements View.OnClickListener{ View v ; @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // TODO Auto-generated method stub View rootView = inflater.inflate(R.layout.child_fragment, container, false); v = rootView; return rootView; } @Override public void onClick(View view) { } }
источник
Используйте getChildFragmentManager (), перейдите по ссылке: Вложенный фрагмент
источник
Ничего сложного. Мы не можем использовать
getFragmentManager()
здесь. Для использования фрагментов внутри фрагмента мы используемgetChildFragmentManager()
. Остальное будет так же.источник
Вы можете добавить
FrameLayout
к фрагменту и заменить его другим фрагментом при его инициализации.Таким образом, вы можете считать, что другой фрагмент находится внутри первого фрагмента.
источник
В настоящее время вложенные фрагменты поддерживаются только в том случае, если они созданы программно! Таким образом, в настоящее время в схеме макета xml не поддерживается макет вложенных фрагментов!
источник
Это может помочь тем, кто работает на Kotlin, вы можете использовать функцию расширения, поэтому создайте файл kotlin, скажем, «util.kt», и добавьте этот фрагмент кода.
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) { val transaction = childFragmentManager.beginTransaction() transaction.replace(frameId, fragment).commit() }
Допустим, это класс ребенка
class InputFieldPresentation: Fragment() { var views: View? = null override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { views = inflater!!.inflate(R.layout.input_field_frag, container, false) return views } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... } ... }
Теперь вы можете добавить детей к отцовскому фрагменту следующим образом
FatherPresentation:Fragment() { ... override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val fieldFragment= InputFieldPresentation() addChildFragment(fieldFragment,R.id.fragmet_field) } ... }
где R.id.fragmet_field - это идентификатор макета, который будет содержать фрагмент. Разумеется, этот текст находится внутри исходного фрагмента. Вот пример
Father_fragment.xml :
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android" > ... <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/fragmet_field" android:orientation="vertical" > </LinearLayout> ... </LinearLayout>
источник
MapFragment
Команда Android говорит, что поддержки нет , начиная с Android 3.0. Здесь больше информации о проблеме. Но что вы можете сделать, создав фрагмент, который возвращаетMapActivity
. Вот пример кода. Спасибо inazaruk:Как это работает :
FragmentActivity
и размещает два MapFragment.MapView.
LocalActivityManagerFragment
хосты LocalActivityManager.LocalActivityManagerFragment
и с помощьюTabHost
создает внутренний экземпляр MyMapActivity.Если у вас есть сомнения, дайте мне знать
источник
Привет, я решил эту проблему, поместив каждый фрагмент в отдельный макет. Я сделал видимым только связанный макет и убрал другие видимости.
Я имею в виду:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:layout_width="wrap_content" android:id="@+id/button1" android:layout_height="wrap_content" android:text="Button1"></Button> <Button android:text="Button2" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Button3" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Button4" android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </LinearLayout> <LinearLayout android:layout_width="full_screen" android:layout_height="0dp" android:layout_weight="1" android:id="action_For_Button1" android:visibility="visible"> <Fragment android:layout_width="full_screen" android:layout_height="full_screen" android:id="fragment1" . . . / > </LinearLayout> <LinearLayout android:layout_width="full_screen" android:layout_height="0dp" android:id="action_For_Button1" android:layout_weight="1" android:visibility="gone"> <Fragment android:layout_width="full_screen" android:layout_height="full_screen" android:id="fragment2" . . . / > </LinearLayout> . . . </LinearLayout>
Я предполагал, что вы откроете свою страницу при нажатии кнопки 1. Вы можете управлять видимостью вашего фрагмента при нажатии кнопки. Вы можете сделать видимым связанный макет, а другие исчезнуть, а с помощью диспетчера фрагментов вы можете взять свой фрагмент. Этот подход сработал для меня. И поскольку представление, которое имеет видимость: ушел, невидимо и не занимает места для макета, я думаю, что этот подход не вызывает проблем с пространством.
PS: Я просто пытался объяснить, что в моем коде решения могут быть синтаксические ошибки или неполная структура.
источник