266

I want to add a Fragment to an Activity that implements its layout programmatically. I looked over the Fragment documentation but there aren't many examples describing what I need. Here is the type of code I tried to write:

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

This code compiles but crashes at start, probably because my FragmentTransaction.add() is incorrect. What is the correct way to do this?

JJD
  • 50,076
  • 60
  • 203
  • 339
Tony Wong
  • 5,444
  • 5
  • 22
  • 18

8 Answers8

209

It turns out there's more than one problem with that code. A fragment cannot be declared that way, inside the same java file as the activity but not as a public inner class. The framework expects the fragment's constructor (with no parameters) to be public and visible. Moving the fragment into the Activity as an inner class, or creating a new java file for the fragment fixes that.

The second issue is that when you're adding a fragment this way, you must pass a reference to the fragment's containing view, and that view must have a custom id. Using the default id will crash the app. Here's the updated code:

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;
        }
    }
}
JJD
  • 50,076
  • 60
  • 203
  • 339
Tony Wong
  • 5,444
  • 5
  • 22
  • 18
  • 118
    If you only want to use the fragment as the top level content view of the activity, then you can use `ft.add(android.R.id.content, newFragment)`. It's only necessary to create a custom layout and setting its id if the fragment's container is not the activity's content view. – Tony Wong Mar 02 '11 at 00:11
  • 25
    Instead of hard coding the id, you can [define it in XML](http://developer.android.com/guide/topics/resources/more-resources.html#Id) and reference it as normal (R.id.myid). – Jason Hanley Mar 02 '11 at 01:58
  • @JasonHanley if I will be creating many views, do you know how I will generate many unique view id's programmatically? – Tony Wong Mar 03 '11 at 07:05
  • 1
    I don't know how to do that, but remember that an id only has to be unique in the scope that you need to use it. – Jason Hanley Mar 03 '11 at 12:58
  • 2
    the id only needs to be unique in its level within the current heirarchy of the containing layout. So say its wrapped in a linear layout, it only needs to be unqiue among the other views within that linear layout. – Shaun Mar 30 '11 at 17:05
  • @TonyWong to create a uniqID its easiest to have a private member of the class DebugExampleTwoFragment that is static and an int. Overload the constructor to first call super() then increment that counter. – retrohacker Aug 03 '12 at 00:54
  • @TonyWong Is it possible to get same behaviour of TabViewActivity using fragmentation, I want to open a new activity in fragmentation area if I select any of tab? – CoDe Oct 06 '12 at 05:59
  • Tony, are you sure about that regarding `android.R.id.content`? From the doc (http://developer.android.com/reference/android/app/FragmentTransaction.html#add(int%2C%20android.app.Fragment)), only this is specified: "Optional identifier of the container this fragment is to be placed in. If 0, it will not be placed in a container.". I don't see where this `android.R.id.content` is coming from... – RedGlyph Aug 30 '13 at 12:39
  • @RedGlyph It's been a while, but I think I got `android.R.id.content` by looking at the Android source code. I seem to recall it's the default id they use in a couple of places. – Tony Wong Sep 02 '13 at 02:36
  • 1
    You can create an ID dynamically using setId(View.NO_ID) and then getId() to see what it was. – enl8enmentnow Dec 18 '14 at 10:31
  • When, in the answer, you say "custom id", are you excluding the `generateViewID` method? I have a crash because it does not find the view with a generated ID. Trying to understand more... – Antonio Sesto Mar 08 '15 at 22:07
  • @AntonioSesto I couldn't use `generateViewID` because it's only available in API 17 and newer. When I wrote the question only API 11 was available. – Tony Wong Mar 16 '15 at 01:00
  • @TonyWong Thank you, but I cannot see how this solution is different from mine. In this solution you have a single ID, in my case I have several of them generated by View.generateID() - which usually starts from 1. – Antonio Sesto Mar 16 '15 at 12:32
76

Here is what I came up with after reading Tony Wong's comment:

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

If you are using Kotlin make sure to take a look at what the Kotlin extensions by Google provide or just write your own.

JJD
  • 50,076
  • 60
  • 203
  • 339
  • Don't do so! Check `if (savedInstanceState == null)` before fragment creation, or after rotating a screen you will have two fragments or fragments reordering. Do not use `add` method at all! Only `replace`. Or you will have weird behaviour. – CoolMind Mar 26 '19 at 08:18
  • Where do you get the value for "backStackStateName"? (When using the replace function) – vikzilla Nov 16 '19 at 01:22
  • @vikzilla You can find pretty good answers [here](https://stackoverflow.com/questions/22984950/what-is-the-meaning-of-addtobackstack-with-null-parameter) and in the [docs](https://developer.android.com/reference/android/app/FragmentTransaction#addToBackStack(java.lang.String)). In short: the `backStackStateName` string is something which is defined by you. – JJD Nov 16 '19 at 08:11
37
    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();
        }
    }

To add a fragment into a Activity or FramentActivity it requires a Container. That container should be a "Framelayout", which can be included in xml or else you can use the default container for that like "android.R.id.content" to remove or replace a fragment in Activity.

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>
anand krish
  • 4,281
  • 4
  • 44
  • 47
33

After read all Answers I came up with elegant way:

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

}

basically you don't need to add a frameLayout as container of your fragment instead you can add straight the fragment into the android root View container

IMPORTANT: don't use replace fragment as most of the approach shown here, unless you don't mind to lose fragment variable instance state during onrecreation process.

Alireza Noorali
  • 3,129
  • 2
  • 33
  • 80
Xenione
  • 2,174
  • 1
  • 23
  • 30
  • thanks for the answer, this adds the fragment tab to the whole screen? but how you add to one frame layout or view pager? – flankechen Apr 11 '20 at 08:19
9

For attaching fragment to an activity programmatically in Kotlin, you can look at the following code:

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

    }
}

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

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

}

If you are familiar with Extensions in Kotlin then you can even better this code by following this article.

bhavya_karia
  • 760
  • 1
  • 6
  • 12
6
public abstract class SingleFragmentActivity extends Activity {

    public static final String FRAGMENT_TAG = "single";
    private Fragment fragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            fragment = onCreateFragment();
           getFragmentManager().beginTransaction()
                   .add(android.R.id.content, fragment, FRAGMENT_TAG)
                   .commit();
       } else {
           fragment = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
       }
   }

   public abstract Fragment onCreateFragment();

   public Fragment getFragment() {
       return fragment;
   }

}

use

public class ViewCatalogItemActivity extends SingleFragmentActivity {
    @Override
    public Fragment onCreateFragment() {
        return new FragmentWorkShops();
    }

}
user2212515
  • 1,220
  • 1
  • 12
  • 10
6

For API level 17 or higher, View.generateViewId() will solve this problem. The utility method provides a unique id that is not used in build time.

Alireza Noorali
  • 3,129
  • 2
  • 33
  • 80
Sfseyhan
  • 1,321
  • 1
  • 13
  • 18
  • 3
    Welcome to Stack Overflow! Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – SuperBiasedMan Sep 07 '15 at 15:53
5

This may help you

Defining a Fragment

create xml file for fragment view fragment_abc.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" >

<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView" />

<Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button" />

</LinearLayout>

create fragment ABCFragment.java

import androidx.fragment.app.Fragment;

public class FooFragment extends Fragment {
// The onCreateView method is called when Fragment should create its View object hierarchy,
// either dynamically or via XML layout inflation. 

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle 
savedInstanceState) {
    // Defines the xml file for the fragment
    return inflater.inflate(R.layout.fragment_abc, parent, false);
}

// This event is triggered soon after onCreateView().
// Any view setup should occur here.  E.g., view lookups and attaching view listeners.
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    // Setup any handles to view objects here
    // EditText etFoo = (EditText) view.findViewById(R.id.etFoo);
}
}

Add frameLayout in your activity

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

now in activity, add following method

protected void setFragment() {
    // Begin the transaction
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    // Replace the contents of the container with the new fragment
    ft.replace(R.id.fragment_container, new ABCFragment());
    // or ft.add(R.id.your_placeholder, new ABCFragment());
    // Complete the changes added above
    ft.commit();
}

reference : https://guides.codepath.com/android/creating-and-using-fragments#defining-a-fragment

Dinesh Sarma
  • 461
  • 3
  • 14