Фрагмент внутри фрагмент

Мне нужна помощь по работе с фрагментом внутри фрагмента, на самом деле я сталкиваюсь с проблемой при нажатии кнопки назад. На главном экране приложения есть кнопки и нажатие на каждом представлении кнопок заменяет новый фрагмент (и этот фрагмент содержит внутри другого фрагмента), динамическое добавление / замена фрагмента работает нормально, при нажатии замененного фрагмента 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: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:id="@+id/linearLayout2"
   android:layout_width="match_parent"
    android:layout_height="wrap_content"
   android:orientation="horizontal"></LinearLayout>
     </LinearLayout>

Надеюсь, я попытался очистить мою проблему.

15 ответов

Решение

AFAIK, фрагменты не могут содержать другие фрагменты.


ОБНОВИТЬ

В текущих версиях пакета поддержки Android - или собственных фрагментов API уровня 17 и выше - вы можете вкладывать фрагменты с помощью getChildFragmentManager(), Обратите внимание, что это означает, что вам нужно использовать версию фрагментов пакета поддержки Android на уровнях API 11-16, потому что, хотя на этих устройствах имеется собственная версия фрагментов, эта версия не имеет getChildFragmentManager(),

Мне нужно было больше контекста, поэтому я сделал пример, чтобы показать, как это делается. Самым полезным, что я прочитал во время подготовки, было следующее:

Деятельность

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 в onViewCreated настроить дочерний фрагмент.

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

Чтобы разместить фрагмент внутри другого фрагмента, используйте 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();
    }

Ты можешь использовать 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) {


    }


} 

Я решил эту проблему. Вы можете использовать библиотеку поддержки и 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 Внутренний фрагмент ChildFragmentManager должны быть переданы ViewPagerAdapter

Ничего сложного. Мы не можем использовать getFragmentManager() Вот. Для использования фрагментов внутри фрагмента мы используем getChildFragmentManager(), Отдых будет таким же.

Используйте getChildFragmentManager(), перейдите по ссылке: Вложенный фрагмент

Можете добавить FrameLayout к фрагменту и замените его другим фрагментом при инициализации.

Таким образом, вы можете считать, что другой фрагмент находится внутри первого фрагмента.

В данный момент вложенные фрагменты поддерживаются только в том случае, если они генерируются программно! Таким образом, в настоящее время макет вложенного фрагмента не поддерживается в схеме компоновки xml!

По словам команды Android, поддержка MapFragment отсутствует с версии Android 3.0. Здесь больше информации о проблеме http://code.google.com/p/android/issues/detail?id=15347&utm_source=buffer&buffer_share=acc72 Но что вы можете сделать, создав Фрагмент, который возвращает MapActivity. Вот пример кода. Спасибо Иназаруку: https://github.com/inazaruk/examples/tree/master/MapFragmentExample

Как это устроено:

-MainFragmentActivity - это действие, которое расширяет FragmentActivity и содержит два MapFragments. -MyMapActivity расширяет MapActivity и имеет MapView. -LocalActivityManagerFragment хостов LocalActivityManager. -MyMapFragment расширяет LocalActivityManagerFragment и с помощью TabHost создает внутренний экземпляр MyMapActivity. если у вас есть какие-либо сомнения, пожалуйста, дайте мне знать

Это может помочь тем, кто работает над 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 - это идентификатор макета, который будет содержать фрагмент. Конечно, этот lyout находится внутри родительского фрагмента. Вот пример

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>

Котлин

В Activity вы используете . В фрагменте Activity используйте . В XML-файлах, которые будут использоватьsupportFragmentManagerиchildFragmentManagerвам нужно добавитьFrameLayout

Пример:

MainActivty — будет содержать FrameLayout, в котором будет размещаться HomeContainerFragment черезsupportFragmentManager.beginTransaction().

HomeContainerFragment — будет содержать FrameLayout, в котором будет размещаться HomeFragment черезchildFragmentManager.beginTransaction(). Вы также можете добавить представления или что-то еще в этот XML и расположить FrameLayout над/под ними, но для простоты я просто использовал FrameLayout без чего-либо еще.

HomeFragment — он будет содержать ваши представления, кнопки и т. д.

XML основной активности:

      <FrameLayout
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

XML-файл HomeContainerFragment:

      <FrameLayout
    android:id="@+id/childFrameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

XML-файл домашнего фрагмента:

      // views, buttons, etc

Основная деятельность:

      override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    addParentFragment()
}

fun addParentFragment() {

    val homeContainerFragment = HomeContainerFragment()

    try {
        supportFragmentManager.beginTransaction()
            .add(binding.frameLayout.id, homeContainerFragment, "homeContainerId")
            .commit()
    } catch (exception: IllegalStateException) {
        println("something went wrong: ${exception.message}")
    }
}

ГлавнаяКонтейнерФрагмент:

      class HomeContainerFragment: Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        addChildFragment()
    }

    fun addChildFragment() {
    
        val homeFragment = HomeFragment()
    
        try {
            childFragmentManager.beginTransaction()
                .add(binding.childFrameLayout.id, homeFragment, "homeId")
                .commit()
        } catch (exception: IllegalStateException) {
            println("something went wrong: ${exception.message}")
        }
    }
}

Важно: это всего лишь общее представление о том, как добавлять фрагменты. Имейте в виду, что когда ваше устройство вращаетсяonViewCreatedпозвонят еще раз, а затемaddChildFragment()позвонят еще раз. Вам нужно использоватьsavedInstanceStateподготовиться к этой ситуации.

Пример:

      class HomeContainerFragment: Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        addChildFragment(savedInstanceState)
    }

    val homeId = "homeId"
    
    fun addChildFragment(savedInstanceState: Bundle?) {
    
        if (savedInstanceState == null && childFragmentManager.findFragmentByTag(homeId) == null {
    
            val homeFragment = HomeFragment()
            
            try {
                childFragmentManager.beginTransaction()
                    .add(binding.childFrameLayout.id, homeFragment, homeId)
                    .commit()
            } catch (exception: IllegalStateException) { }
        }
    }
}
  1. Создайте FragmentA в MainActivity с помощью метода экземпляров STATIC.

  2. Создать дочерние фрагменты: фрагментA1, фрагментA2... (FragmentA имеет макет, содержащий дочерние фрагменты)

  3. Создайте фрагмент набора методов во FragmentA.

  4. Используйте этот метод для установки дочерних фрагментов FragmentA во фрагменте A1, фрагменте A2... Это тот СТАТИЧЕСКИЙ МЕТОД (во FragmentA):

    статический OneFragment oneFragment;

    общественные статические экземпляры OneFragment (){return oneFragment;}

    публичный просмотр onCreateView (oneFragment = this;)

Привет, я решил эту проблему, поместив каждый фрагмент в отдельный макет. И я сделал видимым только что связанный макет, а остальные видимости исчезли.

Я имею в виду:

<?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: я просто пытался объяснить, что мой код решения может иметь синтаксические ошибки или незавершенную структуру.

Другие вопросы по тегам