You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

屏幕旋转时避免生成新GameInstance ID,恢复游戏实例状态的方案

游戏实例状态保持与UI重建解决方案

一、解决屏幕旋转重复生成GameInstanceID的问题

核心是精准区分场景,不在onCreate中无条件生成新ID:

  • 分场景获取ID:
    在onCreate中按优先级判断ID来源:先检查是否从历史游戏列表跳转传入ID,再检查是否是屏幕旋转的状态恢复,最后才是首次启动新游戏生成ID。

    private lateinit var currentGameInstanceId: String
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_game)
    
        currentGameInstanceId = when {
            // 从历史列表进入,直接取传入的ID
            intent.hasExtra("GAME_INSTANCE_ID") -> intent.getStringExtra("GAME_INSTANCE_ID")!!
            // 屏幕旋转重建,恢复之前保存的ID
            savedInstanceState != null -> savedInstanceState.getString("GAME_INSTANCE_ID")!!
            // 首次启动新游戏,生成唯一ID
            else -> UUID.randomUUID().toString()
        }
    
        // 初始化游戏:加载数据、构建UI
        setupGame(currentGameInstanceId)
    }
    
    // 保存ID用于屏幕旋转恢复
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putString("GAME_INSTANCE_ID", currentGameInstanceId)
    }
    
  • 隔离新游戏触发逻辑:
    把“新游戏”按钮的点击事件作为唯一的新游戏入口,点击时才生成新ID并初始化,避免onCreate的默认逻辑干扰。

二、GridLayout替代方案与UI重建优化

如果GridLayout在状态恢复时表现异常,推荐以下两种更可靠的方案:

1. RecyclerView + GridLayoutManager

RecyclerView自带状态管理,适配动态数据更灵活:

  • 封装分数单元格为ViewHolder,用列表承载所有分数数据;
  • 从Room加载游戏数据后,直接绑定到Adapter,RecyclerView自动重建网格布局。
    // 分数数据模型
    data class ScoreRecord(val playerId: String, val round: Int, val score: Int)
    
    // Grid适配器
    class ScoreGridAdapter(private val scores: List<ScoreRecord>) : RecyclerView.Adapter<ScoreGridAdapter.ScoreHolder>() {
        inner class ScoreHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            val scoreTv: TextView = itemView.findViewById(R.id.tv_score)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ScoreHolder {
            val itemView = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_score_cell, parent, false)
            return ScoreHolder(itemView)
        }
    
        override fun onBindViewHolder(holder: ScoreHolder, position: Int) {
            val record = scores[position]
            holder.scoreTv.text = record.score.toString()
            // 可添加分数修改的交互逻辑
        }
    
        override fun getItemCount() = scores.size
    }
    
    // 初始化RecyclerView
    private fun setupGame(gameId: String) {
        val rvGrid = findViewById<RecyclerView>(R.id.rv_score_grid)
        // 列数设为玩家数量,实现网格布局
        rvGrid.layoutManager = GridLayoutManager(this, getPlayerCount(gameId))
        
        // 从Room加载该游戏的所有分数记录
        val scoreRecords = scoreDao.getScoresByGameId(gameId)
        rvGrid.adapter = ScoreGridAdapter(scoreRecords)
    }
    

2. ConstraintLayout 动态生成单元格

如果偏好直接操作视图,可通过ConstraintLayout动态创建分数单元格:

  • 提前计算每个单元格的行列约束;
  • 加载游戏数据后,循环生成TextView并设置约束、绑定分数值,屏幕旋转时重新执行该逻辑即可。

三、完整流程落地(暂停、历史列表)

  • 暂停游戏:无需额外处理,只要确保分数修改实时同步到Room即可,退出时标记游戏实例为“未完成”。
  • 历史游戏列表:
    1. 从Room查询所有游戏实例,按“已完成/未完成”分组展示;
    2. 点击列表项时,通过Intent传递对应gameInstanceId到游戏Activity;
    3. 游戏Activity接收ID后,从Room加载全量数据,重建UI继续游戏。

核心原则:所有游戏状态数据持久化到Room,依赖gameInstanceId作为唯一标识,任何场景下都通过ID拉取完整数据重建状态,避免依赖内存状态保存。

内容的提问来源于stack exchange,提问作者Christof Plieschnegger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:23:15