Android编程笔记(八):Fragment
How to use fragments
A fragment represents a modular portion of the user interface within an activity. A fragment has its own lifecycle, receives its own input events, and you can add or remove fragments while the containing activity is running.
This document describes how to create a fragment and include it in an activity.
Setup your environment
Fragments require a dependency on the AndroidX Fragment library.
To include the AndroidX Fragment library to your project, add the following dependencies in your app’s build.gradle
file:
dependencies {
val fragment_version = "1.8.8"
// Java language implementation
implementation("androidx.fragment:fragment:$fragment_version")
// Kotlin
implementation("androidx.fragment:fragment-ktx:$fragment_version")
}
Create a fragment class
To create a fragment, extend the AndroidX Fragment
class, and override its methods to insert your app logic, similar to the way you would create an Activity
class. To create a minimal fragment that defines its own layout, provide your fragment’s layout resource to the base constructor, as shown in the following example:
class ExampleFragment : Fragment(R.layout.example_fragment)
You can customize the fragment’s view by overriding the onCreateView()
method.
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment.
// or you can inflate a different layout here.
val root: View = inflater.inflate(R.layout.my_fragment_layout, container, false)
// Additional graphical initialisations...
return root
}
}
Difference and uses of onCreate()
, onCreateView()
in fragments
public void onCreate (Bundle savedInstanceState)
Called to do initial creation of a fragment. The onCreate()
method in a Fragment
is called after the Activity
’s onAttachFragment()
but before that Fragment
’s onCreateView()
.
In this method, you can assign variables, get Intent
extras, and anything else that doesn’t involve the View hierarchy (i.e. non-graphical initialisations). This is because this method can be called when the Activity
’s onCreate()
is not finished, and so trying to access the View hierarchy here may result in a crash.
public View onCreateView (LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
Called to have the fragment instantiate its user interface view. After the onCreate()
is called (in the Fragment
), the Fragment
’s onCreateView()
is called. You can assign your View
variables and do any graphical initialisations. You are expected to return a View
from this method, and this is the main UI view, but if your Fragment
does not use any layouts or graphics, you can return null
(happens by default if you don’t override).
To sum up
They are all called in the Fragment
but are called at different times.
The onCreate()
is called first, for doing any non-graphical initialisations. Next, you can assign and declare any View
variables you want to use in onCreateView()
.
Add a fragment to an activity
Generally, your fragment must be embedded within an AndroidX FragmentActivity
to contribute a portion of UI to that activity’s layout. FragmentActivity
is the base class for AppCompatActivity
, so if you’re already subclassing AppCompatActivity
to provide backward compatibility in your app, then you do not need to change your activity base class.
You can add your fragment to the activity’s view hierarchy either by defining the fragment in your activity’s layout file or by defining a fragment container in your activity’s layout file and then programmatically adding the fragment from within your activity. In either case, you need to add a FragmentContainerView
that defines the location where the fragment should be placed within the activity’s view hierarchy. It is strongly recommended to always use a FragmentContainerView
as the container for fragments, as FragmentContainerView
includes fixes specific to fragments that other view groups such as FrameLayout
do not provide.
Add a fragment via XML
To declaratively add a fragment to your activity layout’s XML, use a FragmentContainerView
element.
Here’s an example activity layout containing a single FragmentContainerView
:
<!-- res/layout/example_activity.xml -->
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.ExampleFragment" />
The android:name
attribute specifies the class name of the Fragment
to instantiate. When the activity’s layout is inflated, the specified fragment is instantiated, onInflate()
is called on the newly instantiated fragment, and a FragmentTransaction
is created to add the fragment to the FragmentManager
.
Note: You can use the class
attribute instead of android:name
as an alternative way to specify which Fragment
to instantiate.
Add a fragment programmatically
To programmatically add a fragment to your activity’s layout, the layout should include a FragmentContainerView
to serve as a fragment container, as shown in the following example:
<!-- res/layout/example_activity.xml -->
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Unlike the XML approach, the android:name
attribute isn’t used on the FragmentContainerView
here, so no specific fragment is automatically instantiated. Instead, a FragmentTransaction
is used to instantiate a fragment and add it to the activity’s layout.
While your activity is running, you can make fragment transactions such as adding, removing, or replacing a fragment. In your FragmentActivity
, you can get an instance of the FragmentManager
, which can be used to create a FragmentTransaction
. Then, you can instantiate your fragment within your activity’s onCreate()
method using FragmentTransaction.add()
, passing in the ViewGroup
ID of the container in your layout and the fragment class you want to add and then commit the transaction, as shown in the following example:
class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
supportFragmentManager.commit {
setReorderingAllowed(true)
add<ExampleFragment>(R.id.fragment_container_view)
}
}
}
}
Note: You should always use setReorderingAllowed(true)
when performing a FragmentTransaction
. For more information on reordered transactions, see Fragment transactions.
In the previous example, note that the fragment transaction is only created when savedInstanceState
is null
. This is to ensure that the fragment is added only once, when the activity is first created. When a configuration change occurs and the activity is recreated, savedInstanceState
is no longer null
, and the fragment does not need to be added a second time, as the fragment is automatically restored from the savedInstanceState
.
Fragment manager
Note: We strongly recommend using the Navigation library to manage your app’s navigation. The framework follows best practices for working with fragments, the back stack, and the fragment manager. For more information about Navigation, see Get started with the Navigation component and Migrate to the Navigation component.
FragmentManager
is the class responsible for performing actions on your app’s fragments, such as adding, removing, or replacing them and adding them to the back stack.
You might never interact with FragmentManager
directly if you’re using the Jetpack Navigation library, as it works with the FragmentManager
on your behalf. However, any app using fragments is using FragmentManager
at some level, so it’s important to understand what it is and how it works.
Access the FragmentManager
You can access the FragmentManager
from an activity or from a fragment.
FragmentActivity
and its subclasses, such as AppCompatActivity
, have access to the FragmentManager
through the getSupportFragmentManager()
method.
Fragments can host one or more child fragments. Inside a fragment, you can get a reference to the FragmentManager
that manages the fragment’s children through getChildFragmentManager()
. If you need to access its host FragmentManager
, you can use getParentFragmentManager()
.
Here are a couple of examples to see the relationships between fragments, their hosts, and the FragmentManager
instances associated with each.
Figure 1. Two UI layout examples showing the relationships between fragments and their host activities.
Figure 1 shows two examples, each of which has a single activity host. The host activity in both of these examples displays top-level navigation to the user as a BottomNavigationView
that is responsible for swapping out the host fragment with different screens in the app. Each screen is implemented as a separate fragment.
The host fragment in Example 1 hosts two child fragments that make up a split-view screen. The host fragment in Example 2 hosts a single child fragment that makes up the display fragment of a swipe view.
Given this setup, you can think about each host as having a FragmentManager
associated with it that manages its child fragments. This is illustrated in figure 2 along with property mappings between supportFragmentManager
, parentFragmentManager
, and childFragmentManager
.
Figure 2. Each host has its own FragmentManager
associated with it that manages its child fragments.
The appropriate FragmentManager
property to reference depends on where the callsite is in the fragment hierarchy along with which fragment manager you are trying to access.
Once you have a reference to the FragmentManager
, you can use it to manipulate the fragments being displayed to the user.
Child fragments
Generally speaking, your app consists of a single or small number of activities in your application project, with each activity representing a group of related screens. The activity might provide a point to place top-level navigation and a place to scope ViewModel
objects and other view-state between fragments. A fragment represents an individual destination in your app.
If you want to show multiple fragments at once, such as in a split-view or a dashboard, you can use child fragments that are managed by your destination fragment and its child fragment manager.
Other use cases for child fragments are the following:
- Screen slides, using a
ViewPager2
in a parent fragment to manage a series of child fragment views. - Sub-navigation within a set of related screens.
- Jetpack Navigation uses child fragments as individual destinations. An activity hosts a single parent
NavHostFragment
and fills its space with different child destination fragments as users navigate through your app.
Use the FragmentManager
The FragmentManager
manages the fragment back stack. At runtime, the FragmentManager
can perform back stack operations like adding or removing fragments in response to user interactions. Each set of changes is committed together as a single unit called a FragmentTransaction
. For a more in-depth discussion about fragment transactions, see the fragment transactions guide.
When the user taps the Back button on their device, or when you call FragmentManager.popBackStack()
, the top-most fragment transaction pops off of the stack. If there are no more fragment transactions on the stack, and if you aren’t using child fragments, the Back event bubbles up to the activity. If you are using child fragments, see special considerations for child and sibling fragments.
When you call addToBackStack()
on a transaction, the transaction can include any number of operations, such as adding multiple fragments or replacing fragments in multiple containers.
When the back stack is popped, all these operations reverse as a single atomic action. However, if you committed additional transactions prior to the popBackStack()
call, and if you didn’t use addToBackStack()
for the transaction, these operations don’t reverse. Therefore, within a single FragmentTransaction
, avoid interleaving transactions that affect the back stack with those that don’t.
Perform a transaction
To display a fragment within a layout container, use the FragmentManager
to create a FragmentTransaction
. Within the transaction, you can then perform an add()
or replace()
operation on the container.
For example, a simple FragmentTransaction
might look like this:
supportFragmentManager.commit {
replace<ExampleFragment>(R.id.fragment_container)
setReorderingAllowed(true)
addToBackStack("name") // Name can be null
}
In this example, ExampleFragment
replaces the fragment, if any, that is currently in the layout container identified by the R.id.fragment_container
ID. Providing the fragment’s class to the replace()
method lets the FragmentManager
handle instantiation using its FragmentFactory
. For more information, see the Provide dependencies to your fragments section.
setReorderingAllowed(true)
optimizes the state changes of the fragments involved in the transaction so that animations and transitions work correctly. For more information on navigating with animations and transitions, see Fragment transactions and Navigate between fragments using animations.
Calling addToBackStack()
commits the transaction to the back stack. The user can later reverse the transaction and bring back the previous fragment by tapping the Back button. If you added or removed multiple fragments within a single transaction, all those operations are undone when the back stack is popped. The optional name provided in the addToBackStack()
call gives you the ability to pop back to a specific transaction using popBackStack()
.
If you don’t call addToBackStack()
when you perform a transaction that removes a fragment, then the removed fragment is destroyed when the transaction is committed, and the user cannot navigate back to it. If you do call addToBackStack()
when removing a fragment, then the fragment is only STOPPED
and is later RESUMED
when the user navigates back. Its view is destroyed in this case. For more information, see Fragment lifecycle.
References:
- https://developer.android.com/guide/fragments/create
- https://stackoverflow.com/questions/28929637/difference-and-uses-of-oncreate-oncreateview-and-onactivitycreated-in-fra