264 votes

Comment ajouter un fragment à une activité avec une vue de contenu créée par programme ?

Je veux ajouter un fragment à une activité qui implémente sa mise en page de manière programmatique. J'ai consulté la documentation sur les fragments, mais il n'y a pas beaucoup d'exemples décrivant ce dont j'ai besoin. Voici le type de code que j'ai essayé d'écrire :

public class DebugExampleTwo extends Activity {

    private ExampleTwoFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout frame = new FrameLayout(this);
        if (savedInstanceState == null) {
            mFragment = new ExampleTwoFragment();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(frame.getId(), mFragment).commit();
        }

        setContentView(frame);
    }
}

...

public class ExampleTwoFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, 
                             ViewGroup container, 
                             Bundle savedInstanceState) {
        Button button = new Button(getActivity());
        button.setText("Hello There");
        return button;
    }
}

Ce code se compile mais se plante au démarrage, probablement parce que ma FragmentTransaction.add() est incorrect. Quelle est la manière correcte de procéder ?

208voto

Tony Wong Points 1507

Il s'avère qu'il y a plus d'un problème avec ce code. Un fragment ne peut pas être déclaré de cette façon, dans le même fichier java que l'activité mais pas en tant que classe interne publique. Le framework s'attend à ce que le constructeur du fragment (sans paramètres) soit public et visible. Déplacer le fragment dans l'activité en tant que classe interne, ou créer un nouveau fichier java pour le fragment résout ce problème.

Le deuxième problème est que lorsque vous ajoutez un fragment de cette manière, vous devez passer une référence à la vue contenant le fragment, et cette vue doit avoir un id personnalisé. L'utilisation de l'id par défaut fera planter l'application. Voici le code mis à jour :

public class DebugExampleTwo extends Activity {

    private static final int CONTENT_VIEW_ID = 10101010;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout frame = new FrameLayout(this);
        frame.setId(CONTENT_VIEW_ID);
        setContentView(frame, new LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        if (savedInstanceState == null) {
            Fragment newFragment = new DebugExampleTwoFragment();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(CONTENT_VIEW_ID, newFragment).commit();
        }
    }

    public static class DebugExampleTwoFragment extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            EditText v = new EditText(getActivity());
            v.setText("Hello Fragment!");
            return v;
        }
    }
}

118 votes

Si vous souhaitez uniquement utiliser le fragment en tant que vue de contenu de haut niveau de l'activité, vous pouvez alors utiliser ft.add(android.R.id.content, newFragment) . Il est seulement nécessaire de créer une mise en page personnalisée et de définir son id si le conteneur du fragment n'est pas la vue du contenu de l'activité.

25 votes

Au lieu de coder en dur l'id, vous pouvez le définir en XML et le référencer comme d'habitude (R.id.myid).

0 votes

@JasonHanley si je vais créer de nombreuses vues, savez-vous comment je vais générer de nombreux identifiants de vue uniques de manière programmatique ?

76voto

JJD Points 7539

Voici ce que j'ai trouvé après avoir lu Commentaire de Tony Wong :

public class DebugExampleTwo extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addFragment(android.R.id.content,
                    new DebugExampleTwoFragment(),
                    DebugExampleTwoFragment.FRAGMENT_TAG);
    }

}

...

public abstract class BaseActivity extends Activity {

    protected void addFragment(@IdRes int containerViewId,
                               @NonNull Fragment fragment,
                               @NonNull String fragmentTag) {
        getSupportFragmentManager()
                .beginTransaction()
                .add(containerViewId, fragment, fragmentTag)
                .disallowAddToBackStack()
                .commit();
    }

    protected void replaceFragment(@IdRes int containerViewId,
                                   @NonNull Fragment fragment,
                                   @NonNull String fragmentTag,
                                   @Nullable String backStackStateName) {
        getSupportFragmentManager()
                .beginTransaction()
                .replace(containerViewId, fragment, fragmentTag)
                .addToBackStack(backStackStateName)
                .commit();
    }

}

...

public class DebugExampleTwoFragment extends Fragment {

    public static final String FRAGMENT_TAG = 
        BuildConfig.APPLICATION_ID + ".DEBUG_EXAMPLE_TWO_FRAGMENT_TAG";

    // ...

}

Kotlin

Si vous utilisez Kotlin, n'oubliez pas de jeter un coup d'œil à ce qu'est la fonction Extensions Kotlin par Google fournir ou simplement écrire votre propre.

0 votes

Ne le faites pas ! Vérifiez if (savedInstanceState == null) avant la création du fragment, ou après la rotation d'un écran, vous aurez deux fragments ou des fragments réordonnés. N'utilisez pas add méthode du tout ! Seulement replace . Ou vous aurez un comportement bizarre.

0 votes

Où obtenez-vous la valeur de "backStackStateName" ? (Lorsque vous utilisez la fonction de remplacement)

0 votes

@vikzilla Vous pouvez trouver de très bonnes réponses. aquí et dans le docs . En bref : le backStackStateName La chaîne est quelque chose qui est défini par vous.

37voto

anand krish Points 2304
    public class Example1 extends FragmentActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
          DemoFragment fragmentDemo = (DemoFragment) 
          getSupportFragmentManager().findFragmentById(R.id.frame_container);
          //above part is to determine which fragment is in your frame_container
          setFragment(fragmentDemo);
                       (OR)
          setFragment(new TestFragment1());
        }

        // This could be moved into an abstract BaseActivity 
        // class for being re-used by several instances
        protected void setFragment(Fragment fragment) {
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = 
                fragmentManager.beginTransaction();
            fragmentTransaction.replace(android.R.id.content, fragment);
            fragmentTransaction.commit();
        }
    }

Pour ajouter un fragment dans une Activity ou FramentActivity, il faut un conteneur. Ce conteneur doit être un " Framelayout ", qui peut être inclus dans le xml ou bien vous pouvez utiliser le conteneur par défaut pour cela comme " android.R.id.content " pour supprimer ou remplacer un fragment dans l'activité.

main.xml

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 <!-- Framelayout to display Fragments -->
   <FrameLayout
        android:id="@+id/frame_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/imagenext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_margin="16dp"
        android:src="@drawable/next" />
</RelativeLayout>

32voto

Xenione Points 613

Après avoir lu toutes les réponses, j'ai trouvé un moyen élégant :

public class MyActivity extends ActionBarActivity {

 Fragment fragment ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    FragmentManager fm = getSupportFragmentManager();
    fragment = fm.findFragmentByTag("myFragmentTag");
    if (fragment == null) {
        FragmentTransaction ft = fm.beginTransaction();
        fragment =new MyFragment();
        ft.add(android.R.id.content,fragment,"myFragmentTag");
        ft.commit();
    }

}

En fait, vous n'avez pas besoin d'ajouter un frameLayout comme conteneur de votre fragment, mais vous pouvez ajouter directement le fragment dans le conteneur de la vue racine Android.

IMPORTANT : ne pas utiliser le fragment de remplacement comme la plupart des approches présentées ici, à moins que cela ne vous dérange pas de perdre l'état d'instance de la variable fragment au cours de l'opération. oncreation processus.

9voto

bhavya_karia Points 78

Pour attacher un fragment à une activité de manière programmatique en Kotlin, vous pouvez consulter le code suivant :

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

            // create fragment instance
            val fragment : FragmentName = FragmentName.newInstance()

            // for passing data to fragment
            val bundle = Bundle()
            bundle.putString("data_to_be_passed", DATA)
            fragment.arguments = bundle

            // check is important to prevent activity from attaching the fragment if already its attached
            if (savedInstanceState == null) {
                supportFragmentManager
                    .beginTransaction()
                    .add(R.id.fragment_container, fragment, "fragment_name")
                    .commit()
            }
        }

    }
}

activité_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.MainActivity">

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Nom du fragment.kt

class FragmentName : Fragment() {

    companion object {
        fun newInstance() = FragmentName()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        // receiving the data passed from activity here
        val data = arguments!!.getString("data_to_be_passed")
        return view
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    }

}

Si vous êtes familier avec Extensions en Kotlin, vous pouvez encore améliorer ce code en suivant les instructions suivantes este article.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X