Working with Fragments
1. Creating Fragment Classes
Fragments are reusable components that define part of a UI and behavior in an Android app. They are used within activities to build flexible and modular UIs.
| Property | Description | Notes |
|---|---|---|
| onCreate() | Called when the fragment is first created. | Initialize any necessary components or state here. |
| onCreateView() | Called to create the view hierarchy associated with the fragment. | Returns a View that represents the fragment's UI. |
| onViewCreated() | Called immediately after onCreateView returns. |
Allows you to perform additional setup on the view after it's created. |
| onDestroyView() | Called when the view is being destroyed. | Use to clean up resources related to the view. |
| onDestroy() | Called when the fragment is being destroyed. | Perform final cleanup before the fragment is removed. |
Example: Basic Fragment Class
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize any necessary components or state here
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_my, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Perform additional setup on the view here
}
}
2. Implementing Fragment Lifecycle Methods
| Method | Description | Notes |
|---|---|---|
| onAttach() | Called when the fragment is associated with an Activity | Use to initialize variables or set up listeners |
| onCreate() | Called to do initial creation of the fragment | Good place to initialize state or set up data |
| onCreateView() | Called to create the view hierarchy | Returns the root view of the fragment |
| onViewCreated() | Called after onCreateView | Use to bind data to views or set up listeners |
| onStart() | Called when the fragment becomes visible | Fragment is visible but not interactive |
| onResume() | Called when the fragment is in the foreground | Fragment is fully interactive and active |
| onPause() | Called when the fragment is no longer in the foreground | Good place to save data or stop animations |
| onStop() | Called when the fragment is no longer visible | Fragment is not visible, but may be resumed later |
| onDestroyView() | Called to clean up the view hierarchy | Unbind views and remove listeners |
| onDestroy() | Called to clean up the fragment | Release any resources or references |
| onDetach() | Called when the fragment is disassociated from Activity | Final cleanup before destruction |
Example: Implementing Lifecycle Methods
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("Fragment", "onCreate called")
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_my, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("Fragment", "onViewCreated called")
}
override fun onStart() {
super.onStart()
Log.d("Fragment", "onStart called")
}
override fun onResume() {
super.onResume()
Log.d("Fragment", "onResume called")
}
override fun onPause() {
super.onPause()
Log.d("Fragment", "onPause called")
}
override fun onStop() {
super.onStop()
Log.d("Fragment", "onStop called")
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("Fragment", "onDestroyView called")
}
override fun onDestroy() {
super.onDestroy()
Log.d("Fragment", "onDestroy called")
}
override fun onDetach() {
super.onDetach()
Log.d("Fragment", "onDetach called")
}
}
3. Adding Fragments
Fragments are used to build flexible and modular UIs in Android applications. They can be added to an activity either programmatically or declaratively.
| Method | Description | Code Example |
|---|---|---|
| add() | Adds a fragment to the activity's container | |
| replace() | Replaces a fragment in the container | |
| attach() | Attaches a fragment that was previously detached | |
Note: Always commit the transaction to apply changes.
4. Replacing and Removing Fragments
| Method | Description | Notes |
|---|---|---|
| replace() | Replaces a fragment in the container with a new one | Must be called on a FragmentTransaction |
| remove() | Removes a fragment from the container | Fragment is detached but not destroyed |
| detach() | Detaches a fragment from the UI but keeps it in memory | Can be re-attached later with attach() |
| attach() | Reattaches a previously detached fragment | Fragment's view is recreated |
Example: Replacing a Fragment
val fragment = MyFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.commit()
Example: Removing a Fragment
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (fragment != null) {
val transaction = supportFragmentManager.beginTransaction()
transaction.remove(fragment)
transaction.commit()
}
Note: Always call
commit() after modifying the fragment transaction to apply changes.
Warning: Never call
commit() after the activity's state is saved, as it may throw an exception.
5. Managing Fragment Backstack
Example: Adding Fragment to Backstack
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val fragment = MyFragment()
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack("MyFragment")
.commit()
}
}
| Method | Description | Notes |
|---|---|---|
| addToBackStack(String) | Adds the transaction to the back stack | String is the name of the entry |
| popBackStack() | Removes the top entry from the back stack | Reverses the last transaction |
| popBackStackImmediate() | Same as popBackStack() but executes immediately | Does not block the UI thread |
Note: Back stack entries are managed in a LIFO (Last In, First Out) manner.
Example: Clearing Backstack
supportFragmentManager.popBackStackImmediate(
null,
FragmentManager.POP_BACK_STACK_INCLUSIVE
)
Backstack Management Tips
- Use
addToBackStack()to enable navigation back - Call
popBackStack()to manually navigate back - Use
POP_BACK_STACK_INCLUSIVEto clear all entries
6. Passing Data Between Fragments
| Method | Description | Usage |
|---|---|---|
| Bundle | Used to pass key-value pairs between fragments | |
| Arguments | Access data passed via Bundle in the fragment | val value = arguments?.getString("key") |
Example: Passing Data Using Bundle
class FragmentA : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_a, container, false)
val bundle = Bundle().apply {
putString("message", "Hello from Fragment A")
}
val fragmentB = FragmentB()
fragmentB.arguments = bundle
return view
}
}
Example: Receiving Data in FragmentB
class FragmentB : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_b, container, false)
val message = arguments?.getString("message")
// Use the received message
return view
}
}
7. Communicating with Activity
| Method | Description | Notes |
|---|---|---|
| onAttach(Context) | Called when the fragment is associated with an Activity | Use to initialize variables or set up listeners |
| onDetach() | Called when the fragment is disassociated from an Activity | Use to clean up resources or remove listeners |
| requireActivity() | Returns the attached Activity, throws exception if not attached | Safe to use when you are sure the fragment is attached |
| activity | Property that holds the attached Activity (nullable) | Use with null safety checks |
Example: Accessing Activity from Fragment
class MyFragment : Fragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
val activity = requireActivity() as MyActivity
// Use activity instance
}
}
Example: Handling Activity Callbacks
interface FragmentCallback {
fun onFragmentEvent(data: String)
}
class MyFragment : Fragment() {
private var callback: FragmentCallback? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is FragmentCallback) {
callback = context
}
}
private fun triggerEvent() {
callback?.onFragmentEvent("Hello from Fragment")
}
}
8. Using Fragment Result API
The Fragment Result API allows fragments to communicate with each other and with their host activities without direct references, promoting loose coupling.
| Method | Description | Usage |
|---|---|---|
| setFragmentResult() | Sets a result for a specific key | setFragmentResult("key", bundle) |
| setFragmentResultListener() | Registers a listener for results with a specific key | setFragmentResultListener("key", this) { _, bundle -> ... } |
| clearFragmentResult() | Removes a result for a specific key | clearFragmentResult("key") |
Example: Sending and Receiving Fragment Results
class FragmentA : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Send result
setFragmentResult("data_key", Bundle().apply {
putString("message", "Hello from Fragment A")
})
}
}
class FragmentB : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Listen for result
setFragmentResultListener("data_key") { _, bundle ->
val message = bundle.getString("message")
// Use the received data
}
}
}
Note: The Fragment Result API is available from AndroidX Fragment version 1.3.0+
9. Handling Fragment Transactions
Example: Basic Fragment Transaction
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
val fragment = MyFragment()
transaction.add(R.id.fragment_container, fragment)
transaction.commit()
}
}
| Method | Description | Notes |
|---|---|---|
| beginTransaction() | Starts a new fragment transaction | Must be called before any operations |
| add(containerId, fragment) | Adds a fragment to the container | Container ID must be valid |
| replace(containerId, fragment) | Replaces a fragment in the container | Removes existing fragment and adds new one |
| remove(fragment) | Removes a fragment from the container | Fragment must be added first |
| commit() | Commits the transaction to the fragment manager | Must be called to apply changes |
Note: Always call
commit() to apply the transaction. Use commitNow() for immediate execution.Example: Fragment Transaction with Animation
val transaction = fragmentManager.beginTransaction()
transaction.setCustomAnimations(
R.anim.slide_in_right,
R.anim.slide_out_left
)
transaction.replace(R.id.fragment_container, newFragment)
transaction.commit()
10. Using DialogFragment
| Property | Description | Example |
|---|---|---|
| DialogFragment | Class for creating dialog-style UI components | class MyDialogFragment : DialogFragment() |
| onCreateView | Override to provide custom layout for dialog | |
| onCreateDialog | Override to customize dialog appearance and behavior | |
Example: Creating a Custom DialogFragment
class CustomDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireContext())
.setTitle("Custom Dialog")
.setMessage("This dialog was created with DialogFragment")
.setPositiveButton("OK") { _, _ -> }
.setNegativeButton("Cancel") { _, _ -> }
.create()
}
}
Workflow: Showing a DialogFragment
- Create a subclass of
DialogFragment - Override
onCreateDialogto define dialog behavior - Use
show()on aFragmentManager
Note: DialogFragment is preferred over
AlertDialog for better lifecycle management and configuration changes.