Skip to main content

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
val fragment = MyFragment()
supportFragmentManager.beginTransaction()
    .add(R.id.fragment_container, fragment)
    .commit()
replace() Replaces a fragment in the container
val newFragment = AnotherFragment()
supportFragmentManager.beginTransaction()
    .replace(R.id.fragment_container, newFragment)
    .commit()
attach() Attaches a fragment that was previously detached
val fragment = MyFragment()
supportFragmentManager.beginTransaction()
    .attach(fragment)
    .commit()
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_INCLUSIVE to clear all entries

6. Passing Data Between Fragments

Method Description Usage
Bundle Used to pass key-value pairs between fragments
val bundle = Bundle().apply {
    putString("key", "value")
}
fragment.arguments = bundle
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
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.dialog_layout, container, false)
}
onCreateDialog Override to customize dialog appearance and behavior
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return AlertDialog.Builder(requireContext())
        .setTitle("Alert")
        .setMessage("This is a dialog")
        .setPositiveButton("OK") { _, _ -> }
        .create()
}

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

  1. Create a subclass of DialogFragment
  2. Override onCreateDialog to define dialog behavior
  3. Use show() on a FragmentManager
Note: DialogFragment is preferred over AlertDialog for better lifecycle management and configuration changes.

Was this article helpful?