Fragments trong Android
1. Giới thiệu
Fragment là một phần UI có thể tái sử dụng, có lifecycle riêng, nằm trong Activity.
Lưu ý: Với Jetpack Compose, việc sử dụng Fragment giảm đáng kể. Tuy nhiên, hiểu Fragment vẫn quan trọng cho legacy projects và một số use cases.
2. Khi nào dùng Fragment?
Nên dùng Fragment khi:
- Làm việc với XML Views (legacy)
- Multi-pane layouts (tablet)
- Bottom Navigation với Fragment
- Integration với third-party libraries yêu cầu Fragment
Với Compose:
- Ưu tiên Navigation Compose
- Không cần Fragment cho hầu hết use cases
- Có thể dùng Fragment để host Compose UI nếu cần
3. Tạo Fragment cơ bản
class HomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
HomeScreen()
}
}
}
}4. Fragment Lifecycle
Fragment Added
│
▼
onAttach() → onCreate() → onCreateView() → onViewCreated()
│
▼
onStart() → onResume()
│
▼ (User interacts)
│
onPause() → onStop()
│
▼
onDestroyView() → onDestroy() → onDetach()class MyFragment : Fragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
Log.d(TAG, "onAttach")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "onCreate")
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d(TAG, "onCreateView")
return inflater.inflate(R.layout.fragment_my, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d(TAG, "onViewCreated")
}
override fun onDestroyView() {
super.onDestroyView()
Log.d(TAG, "onDestroyView")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
}
}5. Fragment với Arguments
class DetailFragment : Fragment() {
companion object {
private const val ARG_ID = "id"
fun newInstance(id: Int): DetailFragment {
return DetailFragment().apply {
arguments = Bundle().apply {
putInt(ARG_ID, id)
}
}
}
}
private val itemId: Int by lazy {
arguments?.getInt(ARG_ID) ?: 0
}
override fun onCreateView(...): View {
return ComposeView(requireContext()).apply {
setContent {
DetailScreen(itemId)
}
}
}
}6. FragmentManager
// Trong Activity
supportFragmentManager.commit {
replace(R.id.fragment_container, HomeFragment())
addToBackStack(null)
}
// Trong Fragment
parentFragmentManager.commit {
replace(R.id.fragment_container, DetailFragment.newInstance(123))
addToBackStack(null)
}7. Fragment với ViewModel
class HomeFragment : Fragment() {
// Scoped to Fragment
private val viewModel: HomeViewModel by viewModels()
// Shared with Activity
private val sharedViewModel: SharedViewModel by activityViewModels()
override fun onCreateView(...): View {
return ComposeView(requireContext()).apply {
setContent {
val state by viewModel.uiState.collectAsState()
HomeScreen(state)
}
}
}
}8. Fragment Communication
Shared ViewModel (Recommended)
// SharedViewModel
class SharedViewModel : ViewModel() {
private val _selectedItem = MutableLiveData<Item>()
val selectedItem: LiveData<Item> = _selectedItem
fun selectItem(item: Item) {
_selectedItem.value = item
}
}
// Fragment A
sharedViewModel.selectItem(item)
// Fragment B
sharedViewModel.selectedItem.observe(viewLifecycleOwner) { item ->
// Handle selected item
}Fragment Result API
// Fragment A (listener)
setFragmentResultListener("requestKey") { key, bundle ->
val result = bundle.getString("resultKey")
}
// Fragment B (sender)
setFragmentResult("requestKey", bundleOf("resultKey" to "Result"))9. Bottom Navigation với Fragments
<!-- activity_main.xml -->
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
app:menu="@menu/bottom_nav_menu" />class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
findViewById<BottomNavigationView>(R.id.bottom_nav)
.setupWithNavController(navController)
}
}10. Compose trong Fragment
class ComposeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
MaterialTheme {
MyComposeScreen()
}
}
}
}
}📝 Tóm tắt
| Aspect | Fragment | Compose |
|---|---|---|
| UI | XML Views | Composables |
| Lifecycle | Complex | Simplified |
| Navigation | FragmentManager | NavController |
| State | ViewModel + LiveData | State + ViewModel |
| Reusability | Fragment.newInstance() | @Composable functions |
Khuyến nghị: Với dự án mới, ưu tiên Jetpack Compose và Navigation Compose. Chỉ dùng Fragment khi thực sự cần thiết.
Last updated on