Giới thiệu
Truyền data từ Activity → Fragment dùng Bundle + setArguments() (trước khi add fragment) hoặc Fragment Result API (hiện đại, type-safe). Không dùng static field hoặc singleton - gây leak và lỗi khi recreate.
Các cách truyền dữ liệu
Cách 1: Bundle + setArguments() (Cổ điển, vẫn dùng được)
Phù hợp: Fragment được add lần đầu từ Activity.
Fragment (MyFragment.kt)
package com.example.activitytofragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.example.activitytofragment.databinding.FragmentMyBinding
class MyFragment : Fragment() {
private lateinit var binding: FragmentMyBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = FragmentMyBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Lấy arguments
val message = arguments?.getString("message") ?: "Không có dữ liệu"
binding.tvMessage.text = message
}
}
Layout Fragment (res/layout/fragment_my.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
android:gravity="center">
<TextView
android:id="@+id/tvMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center" />
</LinearLayout>
MainActivity.kt (Gửi data)
package com.example.activitytofragment
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentTransaction
import com.example.activitytofragment.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnSendData.setOnClickListener { sendDataToFragment() }
}
private fun sendDataToFragment() {
val message = binding.etMessage.text.toString().trim()
if (message.isEmpty()) return
// Tạo fragment instance
val fragment = MyFragment()
// Đóng gói data vào Bundle
val args = Bundle().apply {
putString("message", message)
// Có thể put nhiều loại: putInt, putParcelable, putSerializable...
}
fragment.arguments = args // hoặc fragment.setArguments(args)
// Add fragment vào container
supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.commit()
}
}
Layout Activity (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/etMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Nhập tin nhắn gửi Fragment"
android:padding="12dp"
android:background="@drawable/edittext_rounded" />
<Button
android:id="@+id/btnSendData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Gửi dữ liệu sang Fragment" />
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="16dp" />
</LinearLayout>
Cách 2: Fragment Result API (Hiện đại, Type-safe, Khuyến nghị)
Dùng setFragmentResultListener / setFragmentResult - hoạt động qua FragmentManager, survive configuration change.
Fragment nhận kết quả (MyFragment.kt)
class MyFragment : Fragment() {
private lateinit var binding: FragmentMyBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Đăng ký listener CHO TRƯỚC khi Activity gửi
setFragmentResultListener("message_key") { requestKey, bundle ->
val message = bundle.getString("message") ?: ""
// Cập nhật UI (cần view đã tạo)
binding?.tvMessage?.text = message
}
}
override fun onCreateView(...) { ... }
}
Activity gửi kết quả (MainActivity.kt)
binding.btnSendData.setOnClickListener {
val message = binding.etMessage.text.toString()
// Gửi kết quả đến Fragment đang hiển thị trong container
supportFragmentManager.setFragmentResult("message_key",
Bundle().apply { putString("message", message) }
)
}
Cách 3: Shared ViewModel (Best cho komunikasi 2 chiều, lifecycle-aware)
Activity và Fragment share cùng một ViewModel instance.
SharedViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class SharedViewModel : ViewModel() {
val message = MutableLiveData()
}
Activity
private val viewModel: SharedViewModel by activityViewModels()
binding.btnSendData.setOnClickListener {
viewModel.message.value = binding.etMessage.text.toString()
}
Fragment
private val viewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.message.observe(viewLifecycleOwner) { message ->
binding.tvMessage.text = message
}
}
So sánh 3 cách
| Tiêu chí | Bundle + setArguments | Fragment Result API | Shared ViewModel |
|---|---|---|---|
| Độ phức tạp | Thấp | Trung bình | Cao hơn |
| Survive config change | Có (Bundle tự restore) | Có | Có (ViewModel survive) |
| Type-safe | Không (string key) | Không (string key) | Có (LiveData |
| 2 chiều (Fragment → Activity) | Khó (interface/callback) | Có (setFragmentResult) | Dễ dàng (LiveData) |
| Phù hợp | Data đơn giản, one-time init | Request/Response, callback | Shared state, complex UI |
Truyền Object (Parcelable/Serializable)
// Bundle
val args = Bundle().apply {
putParcelable("user", user) // Parcelable
// putSerializable("user", user) // Serializable
}
fragment.arguments = args
// Fragment nhận
val user = arguments?.getParcelable("user")
Lưu ý quan trọng
- Luôn set arguments trước khi add fragment vào FragmentManager.
- Fragment recreate (rotate screen): arguments tự động restore.
- Không dùng
fragment.message = "..."(property trực tiếp) - mất data khi recreate. - Fragment Result API: Key phải unique (dùng constant string).
- Shared ViewModel: Dùng
activityViewModels()(Activity scope) hoặcviewModels()(Fragment scope). - Navigation Component: Dùng
Safe Args(type-safe navigation arguments) - khuyến nghị nhất cho project mới.
Navigation Component + Safe Args (Best Practice hiện tại)
// nav_graph.xml
// Activity - Navigate with args
val action = MainFragmentDirections.actionMainToMyFragment(message = "Hello")
findNavController().navigate(action)
// Fragment - Receive args (type-safe)
val message = MyFragmentArgs.fromBundle(arguments).message