How do I add a Fragment to an Activity with a programmatically created content view
AndroidAndroid 3.0-HoneycombAndroid FragmentsAndroid Problem Overview
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?
Android Solutions
Solution 1 - Android
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;
}
}
}
Solution 2 - Android
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.
Solution 3 - Android
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>
Solution 4 - Android
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.
Solution 5 - Android
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.
Solution 6 - Android
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();
}
}
Solution 7 - Android
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.
Solution 8 - Android
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