Fragmento dentro do fragmento

137

Preciso de ajuda para trabalhar no fragmento dentro do fragmento, na verdade, estou enfrentando um problema ao pressionar o botão Voltar. A tela principal do aplicativo possui botões e, ao pressionar cada visualização de botão, substituir por novo fragmento (e esse fragmento contém dentro de outro fragmento); adicionar / substituir dinamicamente o fragmento está funcionando bem, pressionando o fragmento do botão 1 substituído, o mesmo acontece ao pressionar o botão, mas se eu pressionar o botão novamente, recebeu uma exceção:

"Duplicate id 0x7f05000a, tag null, or parent id 0x7f050009 with
another fragment for com........ fragmentname"

significa que fragmentos ou fragmentos internos já foram adicionados e eu estou tentando adicioná-los novamente, alguém tem ideia de como trabalhar com fragmentos dentro de fragmentos e se mover para frente e para trás sem nenhum problema, obrigado pelo suporte.

MainActivity, onde fragmentos são dinâmicos adicionados e substituídos.

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

Aqui está o layout: 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>

Espero ter tentado resolver meu problema.

Ijaz Ahmed
fonte
1
código acima está funcionando perfeitamente bem para mim com o Android 3.1
Vivek
3
possível duplicação de fragmentos dentro de fragmentos
Vladimir
para mais detalhes consulte stackoverflow.com/a/11020531/1219456
Raneez Ahmed,

Respostas:

284

AFAIK, fragmentos não podem conter outros fragmentos.


ATUALIZAR

Com as versões atuais do pacote de suporte do Android - ou fragmentos nativos na API nível 17 e superior - você pode aninhar fragmentos por meio de getChildFragmentManager() . Observe que isso significa que você precisa usar a versão dos fragmentos do pacote de suporte do Android nos níveis de API 11-16, porque mesmo que exista uma versão nativa dos fragmentos nesses dispositivos, essa versão não possui getChildFragmentManager().

CommonsWare
fonte
30
A plataforma realmente é péssima aqui. Passei três horas depurando isso porque o fragmento interno ficaria bem na primeira vez, mas desapareceria após uma alteração na orientação da tela. Sem exceção, informações de log, nada. Mudar para getChildFragmentManager()e remover setRetainInstance(true)do fragmento interno (pena) o consertou. Obrigado por salvar meu bacon novamente, @CommonsWare.
Felix
82

insira a descrição da imagem aqui

Como precisava de mais contexto, fiz um exemplo para mostrar como isso é feito. A coisa mais útil que li durante a preparação foi esta:

Atividade

activity_main.xml

Adicione a FrameLayoutà sua atividade para armazenar o fragmento pai.

<?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

Carregue o fragmento pai e implemente os ouvintes do fragmento. (Veja comunicação por fragmentos .)

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

Fragmento pai

fragment_parent.xml

Adicione outro FrameLayoutcontêiner para o fragmento filho.

<?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

Use getChildFragmentManagerem onViewCreatedconfigurar o fragmento criança.

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

Fragmento de criança

fragment_child.xml

Não há nada de especial aqui.

<?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

Também não há nada de especial aqui.

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

Notas

  • A biblioteca de suporte está sendo usada para que fragmentos aninhados possam ser usados ​​antes do Android 4.2.
Suragch
fonte
67

Como os fragmentos aninhados do Android 4.2 (API 17) ficam disponíveis http://developer.android.com/about/versions/android-4.2.html#NestedFragments

Para colocar um fragmento dentro de outro fragmento, use getChildFragmentManager ()

Também está disponível na biblioteca de suporte!

Nik
fonte
Atualize a sua resposta de mencionar que lib apoio já suporta fragements aninhados de 1.6+ android
FindOutIslamNow
13

Fragmentos podem ser adicionados dentro de outros fragmentos, mas você precisará removê-lo do Fragmento pai sempre que o onDestroyView()método do fragmento pai for chamado. E, novamente, adicione-o no Parent FragmentonCreateView() método .

Apenas faça assim:

@Override
    public void onDestroyView()
    {
                FragmentManager mFragmentMgr= getFragmentManager();
        FragmentTransaction mTransaction = mFragmentMgr.beginTransaction();
                Fragment childFragment =mFragmentMgr.findFragmentByTag("qa_fragment")
        mTransaction.remove(childFragment);
        mTransaction.commit();
        super.onDestroyView();
    }
Napolean
fonte
4
Isto não funcionou para mim. Eu tinha um fragmento que inflava uma visão contendo duas visões filhos. Nele onActivityCreated()foram adicionados dois fragmentos, um em cada uma das visualizações, usando getFragmentManager(). Isso funcionou pela primeira vez, mas na rotação e no resumo, apenas um dos fragmentos estava visível. O comportamento estava correto em getChildFragmentManager()vez disso. A abordagem sugerida aqui lançou uma exceção, pois a atividade onSaveInstanceState()já havia sido chamada. A confirmação da transação commitAllowingStateLoss()evitou a exceção, mas não resolveu o problema original.
User1978019
8

Eu resolvi esse problema. Você pode usar a biblioteca de suporte e ViewPager. Se você não precisar deslizar com um gesto, pode desativá-lo. Então, aqui está um código para melhorar minha solução:

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

PSI É um código feio para teste, mas melhora que é possível.

O fragmento PPS Inside ChildFragmentManagerdeve ser passado paraViewPagerAdapter

Vetalll
fonte
8

você pode usar a getChildFragmentManager()função

exemplo:

Fragmento pai:

@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;
}

Layout pai ( 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>

Fragmento de criança:

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) {


    }


} 
S.M_Emamian
fonte
4

Não é nada complicado. Não podemos usar getFragmentManager()aqui. Para usar fragmentos dentro de um fragmento , usamos getChildFragmentManager(). O resto será o mesmo.

Wijay Sharma
fonte
3

Você pode adicionar FrameLayoutao fragmento e substituí-lo por outro fragmento quando inicializar.

Dessa forma, você pode considerar o outro fragmento dentro do primeiro fragmento.

LoveBigPizza
fonte
2

Atualmente no fragmento aninhado, o (s) aninhado (s) só é suportado se forem gerados programaticamente! Portanto, neste momento, nenhum layout de fragmento aninhado é suportado no esquema de layout xml!

Andrea Bellitto
fonte
1

Isso pode ajudar quem trabalha no Kotlin. Você pode usar a função de extensão, então crie um arquivo kotlin, digamos "util.kt" e adicione este pedaço de código.

fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {

    val transaction = childFragmentManager.beginTransaction()
    transaction.replace(frameId, fragment).commit()
}

Digamos que esta é a classe da criança

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)
        ...
    }
    ...
}

Agora você pode adicionar os filhos ao fragmento pai como este

 FatherPresentation:Fragment()
{
  ...

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val fieldFragment= InputFieldPresentation()
        addChildFragment(fieldFragment,R.id.fragmet_field)
    }
...
}

onde R.id.fragmet_field é o ID do layout que conterá o fragmento. Esse lyout está dentro do fragmento pai, é claro. Aqui está um exemplo

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>
DINA TAKLIT
fonte
1

Não há suporte para MapFragment, a equipe do Android diz que está trabalhando desde o Android 3.0. Aqui estão mais informações sobre o problema. Mas o que você pode fazer criando um fragmento que retorna a MapActivity. Aqui está um exemplo de código. Graças a inazaruk:

Como funciona :

  • MainFragmentActivity é a atividade que estende FragmentActivitye hospeda dois MapFragments.
  • MyMapActivity estende o MapActivity e tem MapView.
  • LocalActivityManagerFragment hospeda LocalActivityManager.
  • MyMapFragment estende LocalActivityManagerFragmente, com a ajuda de TabHostcria uma instância interna de MyMapActivity.

Se você tiver alguma dúvida, por favor me avise

rallat
fonte
0

Olá, eu resolvi esse problema colocando por fragmento em um layout distinto. E fiz apenas um layout relacionado visível e fiz com que as outras visibilidades desaparecessem.

Quero dizer:

<?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>

Eu assumi que você abriria sua página quando o botão 1 fosse clicado. Você pode controlar as visibilidades de seu fragmento ao clicar em ação. Você pode tornar o Layout relacionado visível e os outros desaparecidos e, pelo Fragment Manager, você pode pegar seu fragmento. Essa abordagem funcionou para mim. E como a visão que tem visibilidade: ido é invisível e não ocupa espaço para fins de layout, acho que essa abordagem não causa nenhum problema de espaço.

PS: Eu apenas tentei explicar o código da minha solução pode ter erros de sintaxe ou estrutura incompleta.

Sevil
fonte