Kotlin实现Activity间数据回传并返回MainActivity更新列表
问题原因
- 现有代码的核心错误是返回数据时重新创建了MainActivity实例跳转,而非将数据回传给已存在的原MainActivity,会直接导致回退栈混乱、原有页面状态丢失,加上你直接传递EditText的Editable类型数据、没有做类型转换,很容易触发崩溃。
- 你在MainActivity中预先定义的两个变量
enteredPlayerNumber、enteredPlayerName根本不会被赋值,因为你跳转的是全新的MainActivity,和原来打开AddPlayerActivity的实例不是同一个对象。 - 额外问题:直接在onCreate中调用
ViewModel()构造实例是错误用法,配置变更(比如屏幕旋转)时数据会丢失,必须通过系统提供的ViewModelProvider获取实例。
实现步骤
使用官方推荐的Activity Result API实现数据回传,不需要提前定义变量等待数据,只有当AddPlayerActivity关闭返回时才会触发回调读取数据,完全避免提前读取的问题。
1. 修改MainActivity代码
class MainActivity : AppCompatActivity() { // 定义传值Key,避免硬编码写错 companion object { const val EXTRA_PLAYER_NAME = "USERNAME" const val EXTRA_PLAYER_NUMBER = "USER_NUMBER" } // 注册页面返回结果回调 private val addPlayerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK) { val data = result.data ?: return@registerForActivityResult // 从返回的Intent中取值,做类型转换 val playerName = data.getStringExtra(EXTRA_PLAYER_NAME) ?: return@registerForActivityResult val playerNumber = data.getIntExtra(EXTRA_PLAYER_NUMBER, 0) // 将数据添加到ViewModel的列表中 (myViewModel.myList.value as? MutableList)?.add(Player(playerNumber, playerName)) // 触发LiveData观察者刷新列表,如果使用StateFlow等其他可观察类型对应调整即可 myViewModel.myList.value = myViewModel.myList.value } } private lateinit var myViewModel: MyViewModel private lateinit var myRecyclerView: RecyclerView private lateinit var btnAdd: Button override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) myRecyclerView = findViewById(R.id.recyclerView) // 正确获取ViewModel实例,不要直接构造 myViewModel = ViewModelProvider(this)[MyViewModel::class.java] btnAdd = findViewById(R.id.btnAdd) myRecyclerView.layoutManager = LinearLayoutManager(this) myViewModel.getUserData() btnAdd.setOnClickListener { val myIntent = Intent(this, AddPlayerActivity::class.java) // 用注册好的launcher打开AddPlayerActivity,不要直接调用startActivity addPlayerLauncher.launch(myIntent) } myViewModel.myList.observe(this) { myRecyclerView.adapter = MyAdapter(it) } } }
注意:上述代码中的
Player是你的玩家数据实体类,替换成你项目中实际定义的类即可。
2. 修改AddPlayerActivity代码
class AddPlayerActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_add_player) val numberAddPlayer = findViewById<EditText>(R.id.etNumberAdd2) val nameAddPlayer = findViewById<EditText>(R.id.etNameAdd2) val btn = findViewById<Button>(R.id.btnSubmitAdd2) btn.setOnClickListener { // 基础输入校验,避免空内容或格式错误提交 val inputName = nameAddPlayer.text.toString().trim() val inputNumberStr = numberAddPlayer.text.toString().trim() if (inputName.isEmpty() || inputNumberStr.isEmpty()) { Toast.makeText(this, "请输入完整的玩家信息", Toast.LENGTH_SHORT).show() return@setOnClickListener } val inputNumber = inputNumberStr.toIntOrNull() ?: run { Toast.makeText(this, "号码必须为数字", Toast.LENGTH_SHORT).show() return@setOnClickListener } // 构造返回用Intent,不要重新创建MainActivity的Intent val resultIntent = Intent() resultIntent.putExtra(MainActivity.EXTRA_PLAYER_NAME, inputName) resultIntent.putExtra(MainActivity.EXTRA_PLAYER_NUMBER, inputNumber) // 设置返回结果码 setResult(RESULT_OK, resultIntent) // 关闭当前页面,自动回到原有MainActivity实例 finish() } } }
关键注意点
- 页面间回传数据绝对不要在返回时重新启动上一个页面,会导致回退栈堆积、状态丢失,正确逻辑是关闭当前页面,通过系统提供的结果回调把数据传回已经存在的上一个页面实例。
- 从EditText获取输入内容时,必须调用
toString()转成字符串,不能直接传递text属性(它是Editable类型,跨Activity传递时容易触发类型转换异常)。 - 数字类型输入要做格式校验,避免用户输入非数字内容时调用
toInt()触发崩溃。 - 只有当AddPlayerActivity执行完提交、关闭页面后,MainActivity的结果回调才会执行,完全不会出现提前读取变量的问题。
内容的提问来源于stack exchange,提问作者M.hossein Karimi
相关产品推荐
相关产品推荐

