Android类棋类双计时器应用开发问询:时间转移功能实现
实现类棋类双计时器(带时间转移功能)的方案
这是个挺有意思的计时规则变种,我之前做棋类工具类应用时也琢磨过类似需求,给你一套可行的Android平台实现思路:
首先咱们先明确核心规则(基于你描述的“时间转移”需求):当玩家结束回合时,将自己回合内消耗的时间从自身剩余时间中扣除,并直接加到对方的剩余时间中——如果你的规则是其他变种(比如己方消耗的时间仅让对方可用时间增加、不扣除己方时间),逻辑可以轻松微调。
一、核心数据结构与状态管理
先封装一个计时器管理类,把双方的时间状态、回合状态统一管理,避免UI层逻辑混乱:
// 单个玩家的计时器数据 data class PlayerTimer( var remainingTime: Long, // 剩余时间,单位毫秒 val initialTime: Long // 初始时间,用于重置 ) // 回合状态枚举 enum class TurnState { PLAYER_1_TURN, PLAYER_2_TURN, IDLE } // 双计时器核心管理器 class DualTimerManager(private val initialTimeMs: Long) { private var player1Timer = PlayerTimer(initialTimeMs, initialTimeMs) private var player2Timer = PlayerTimer(initialTimeMs, initialTimeMs) private var currentTurn = TurnState.IDLE private var turnStartTime: Long = 0L // 记录当前回合启动的时间戳 private val timerHandler = Handler(Looper.getMainLooper()) private var updateRunnable: Runnable? = null // 对外暴露的状态获取方法 fun getPlayer1Time(): Long = player1Timer.remainingTime fun getPlayer2Time(): Long = player2Timer.remainingTime fun getCurrentTurn(): TurnState = currentTurn }
二、回合启动/停止的核心逻辑
这部分是实现时间转移的关键,当玩家点击按钮时,执行以下流程:
1. 启动回合
记录当前时间戳,启动定时任务实时更新UI,切换回合状态
2. 停止回合
计算当前回合消耗的时间,执行时间转移操作,切换到对方回合
在DualTimerManager中补充核心方法:
// 启动指定玩家的回合 fun startTurn(player: TurnState) { if (currentTurn != TurnState.IDLE) return // 防止重复启动 currentTurn = player turnStartTime = System.currentTimeMillis() // 启动每秒一次的UI更新任务 updateRunnable = object : Runnable { override fun run() { val elapsed = System.currentTimeMillis() - turnStartTime // 实时计算当前回合玩家的剩余时间(扣除已消耗时长) when (currentTurn) { TurnState.PLAYER_1_TURN -> { val tempRemaining = player1Timer.remainingTime - elapsed if (tempRemaining <= 0) { stopTurn() onGameEnded?.invoke(currentTurn) // 触发时间耗尽回调 } onTimeUpdated?.invoke(player1Timer.remainingTime - elapsed, player2Timer.remainingTime) } TurnState.PLAYER_2_TURN -> { val tempRemaining = player2Timer.remainingTime - elapsed if (tempRemaining <= 0) { stopTurn() onGameEnded?.invoke(currentTurn) } onTimeUpdated?.invoke(player1Timer.remainingTime, player2Timer.remainingTime - elapsed) } else -> {} } timerHandler.postDelayed(this, 1000) } } timerHandler.post(updateRunnable!!) } // 停止当前回合,执行时间转移 fun stopTurn() { if (currentTurn == TurnState.IDLE) return val elapsedTime = System.currentTimeMillis() - turnStartTime // 核心:执行时间转移逻辑 when (currentTurn) { TurnState.PLAYER_1_TURN -> { player1Timer.remainingTime -= elapsedTime player2Timer.remainingTime += elapsedTime } TurnState.PLAYER_2_TURN -> { player2Timer.remainingTime -= elapsedTime player1Timer.remainingTime += elapsedTime } } // 停止定时更新,切换回合状态 updateRunnable?.let { timerHandler.removeCallbacks(it) } updateRunnable = null currentTurn = when (currentTurn) { TurnState.PLAYER_1_TURN -> TurnState.PLAYER_2_TURN TurnState.PLAYER_2_TURN -> TurnState.PLAYER_1_TURN else -> TurnState.IDLE } // 通知UI最终时间更新 onTimeUpdated?.invoke(player1Timer.remainingTime, player2Timer.remainingTime) } // 对外暴露的回调接口,用于通知UI更新和游戏结束 var onTimeUpdated: ((player1Time: Long, player2Time: Long) -> Unit)? = null var onGameEnded: ((loser: TurnState) -> Unit)? = null
三、UI层交互实现
在Activity/Fragment中绑定按钮点击事件和UI显示,完成交互闭环:
class TimerActivity : AppCompatActivity() { private lateinit var dualTimerManager: DualTimerManager private val initialTimeMs = 10 * 60 * 1000 // 初始10分钟 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_timer) dualTimerManager = DualTimerManager(initialTimeMs) // 玩家1按钮点击逻辑 btnPlayer1.setOnClickListener { when (dualTimerManager.getCurrentTurn()) { TurnState.IDLE -> dualTimerManager.startTurn(TurnState.PLAYER_1_TURN) TurnState.PLAYER_1_TURN -> dualTimerManager.stopTurn() else -> {} // 非当前回合,不响应 } } // 玩家2按钮点击逻辑 btnPlayer2.setOnClickListener { when (dualTimerManager.getCurrentTurn()) { TurnState.IDLE -> dualTimerManager.startTurn(TurnState.PLAYER_2_TURN) TurnState.PLAYER_2_TURN -> dualTimerManager.stopTurn() else -> {} } } // 时间更新回调,刷新UI dualTimerManager.onTimeUpdated = { player1Time, player2Time -> tvPlayer1Time.text = formatTime(player1Time) tvPlayer2Time.text = formatTime(player2Time) } // 时间耗尽回调,处理游戏结束逻辑 dualTimerManager.onGameEnded = { loser -> val message = if (loser == TurnState.PLAYER_1_TURN) "玩家1时间耗尽!" else "玩家2时间耗尽!" Toast.makeText(this, message, Toast.LENGTH_LONG).show() resetTimer() } // 初始化显示时间 tvPlayer1Time.text = formatTime(initialTimeMs) tvPlayer2Time.text = formatTime(initialTimeMs) } // 格式化时间为MM:SS格式 private fun formatTime(timeMs: Long): String { val minutes = (timeMs / 1000) / 60 val seconds = (timeMs / 1000) % 60 return String.format("%02d:%02d", minutes, seconds) } // 重置计时器 private fun resetTimer() { dualTimerManager.apply { player1Timer = PlayerTimer(initialTimeMs, initialTimeMs) player2Timer = PlayerTimer(initialTimeMs, initialTimeMs) currentTurn = TurnState.IDLE updateRunnable?.let { timerHandler.removeCallbacks(it) } updateRunnable = null onTimeUpdated?.invoke(initialTimeMs, initialTimeMs) } } // 页面销毁时清理定时器 override fun onDestroy() { super.onDestroy() dualTimerManager.updateRunnable?.let { dualTimerManager.timerHandler.removeCallbacks(it) } } }
四、关键注意事项
- 线程安全:这里用主线程的
Handler更新UI,逻辑简单无需后台线程;如果需要更复杂的计算,建议用Coroutine处理。 - 时间精度:如果需要更高的刷新频率(比如每秒多次),可以把
postDelayed的间隔改小(比如100ms),但注意控制性能消耗。 - 状态保存:如果Activity重建(比如旋转屏幕),需要在
onSaveInstanceState中保存当前时间和回合状态,避免数据丢失。 - 规则变种:如果你的需求是“己方消耗的时间仅增加对方可用时间、不扣除己方时间”,只需修改
stopTurn中的逻辑,去掉己方时间扣除的代码即可。
内容的提问来源于stack exchange,提问作者Milan Maxa Maksimović
相关产品推荐
相关产品推荐

