Android开发实战:五子棋小游戏完整项目
简介:本项目是一款基于Android平台的五子棋小游戏,使用Android Studio开发,结合Java或Kotlin语言实现。应用包含完整的游戏逻辑与交互功能,支持两人对战模式,具备良好的用户界面和响应式布局。项目涵盖Android开发的多个核心技术点,包括界面设计、触控事件处理、胜负判断算法、构建与发布流程,适合用于掌握移动端游戏开发全流程的实战学习。 
1. Android五子棋游戏开发概述
五子棋作为一款经典的策略棋类游戏,因其规则简洁、逻辑清晰,深受各年龄层用户的喜爱。随着移动互联网的普及,将五子棋移植到Android平台,不仅具备良好的用户基础,也为开发者提供了一个实践Android应用开发技巧的理想项目。
本章将介绍为何选择Android平台进行五子棋开发,涵盖其在移动设备上的交互特性与开发便捷性,并明确项目目标:实现落子、悔棋、重置、胜负判断等核心功能。同时,将说明开发工具(如Android Studio)与编程语言(Kotlin为主)的选型依据,为后续章节的开发打下坚实基础。
2. Android开发环境搭建与项目结构配置
Android开发的第一步是搭建开发环境。一个稳定、高效的开发环境对于五子棋这类小游戏的快速迭代和调试至关重要。本章将详细介绍如何安装和配置 Android Studio,创建五子棋项目并配置其基础结构,同时探讨开发过程中常见的问题及其解决方案。
2.1 Android Studio安装与配置
作为 Android 开发的官方推荐 IDE,Android Studio 提供了完整的开发、调试和测试工具链。其基于 IntelliJ IDEA 构建,支持 Kotlin、Java 等主流语言,是开发 Android 应用的首选工具。
2.1.1 下载与安装Android Studio
访问 Android Studio 官网 下载适用于你操作系统的安装包(Windows、macOS 或 Linux)。下载完成后,按照安装向导逐步完成安装流程。
提示 :建议选择“Custom”安装选项,手动选择安装组件以节省磁盘空间。
安装完成后启动 Android Studio,首次启动会提示选择是否导入已有的配置。如果是首次使用,选择“Do not import settings”即可。
2.1.2 配置JDK与Android SDK
Android Studio 安装完成后,默认会安装所需的 JDK 和 Android SDK。但为了确保环境稳定,建议检查以下配置:
JDK配置
- Android Studio 从 2020 年起内置了 JetBrains Runtime(JBR),兼容性较好。
- 如果需要使用特定版本的 JDK(如 JDK 11),可在
File > Project Structure > SDKs中添加。
Android SDK配置
- 打开 Settings (Preferences on macOS)。
- 进入 Appearance & Behavior > System Settings 。
- 确认 SDK 路径是否正确(默认路径为
C:\Users\用户名\AppData\Local\Android\Sdk)。 - 在 Android SDK 页面中选择需要的 SDK 版本(建议选择 Android 10 及以上)。
- 安装必要的构建工具(Build-Tools)、平台工具(Platform-Tools)等。
注意 :部分插件或依赖库可能要求特定版本的 SDK 工具,安装时请保持 Build-Tools 与 Gradle 插件版本兼容。
2.1.3 创建并配置AVD虚拟设备
为了在本地测试应用,需要创建 Android Virtual Device(AVD)。
创建 AVD 的步骤如下:
- 打开 Tools > Device Manager 。
- 点击 Create device 。
- 选择设备型号(如 Pixel 3a)和系统镜像(建议选择 Google Play 版本)。
- 设置内存大小、SD 卡容量等参数。
- 完成后点击 Finish ,设备将出现在设备列表中。
提示 :若遇到 AVD 启动缓慢问题,可尝试启用硬件加速(在 BIOS 中开启虚拟化支持)。
示例代码:查看 AVD 命令行信息
# 查看已创建的 AVD 列表
emulator -list-avds
# 启动指定 AVD
emulator -avd Pixel_3a_API_34
2.2 新建五子棋项目并设置基本结构
完成开发环境配置后,接下来将创建五子棋游戏项目并配置其基本结构。
2.2.1 使用Empty Activity模板创建项目
- 打开 Android Studio,选择 New Project 。
- 选择 Empty Activity 模板。
- 设置项目名称为
FiveInARow。 - 选择语言为 Kotlin (推荐)或 Java。
- 设置最小 SDK 为 API 21(Android 5.0)以上以支持现代特性。
- 完成项目创建。
提示 :Kotlin 语言简洁、安全且与 Java 完全兼容,推荐用于新项目开发。
2.2.2 项目目录结构解析(Gradle配置、资源目录等)
创建项目后,可以看到如下主要目录结构:
| 目录 | 描述 |
|---|---|
app/src/main/java |
Java/Kotlin 源码目录 |
app/src/main/res |
资源文件目录(布局、图片、字符串等) |
app/src/main/AndroidManifest.xml |
应用清单文件 |
build.gradle (Project) |
项目级 Gradle 构建脚本 |
build.gradle (Module: app) |
模块级 Gradle 构建脚本 |
gradle.properties |
Gradle 构建配置文件 |
示例代码:查看 build.gradle (Module: app) 内容
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.fiveinarow'
compileSdk 34
defaultConfig {
applicationId "com.example.fiveinarow"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
}
代码逻辑分析:
namespace:应用程序的包名。compileSdk:编译所使用的 Android SDK 版本。defaultConfig:应用的基础配置信息。buildTypes:构建类型配置(如 debug 和 release)。dependencies:模块依赖项,包括 Kotlin、Material Design 等库。
2.2.3 添加必要的依赖库与支持库
为了增强五子棋游戏的功能,我们可以添加以下常用依赖库:
| 库 | 功能 |
|---|---|
androidx.lifecycle:lifecycle-viewmodel-ktx |
ViewModel 管理游戏状态 |
androidx.lifecycle:lifecycle-livedata-ktx |
LiveData 实现数据驱动 UI |
androidx.activity:activity-ktx |
更简洁的 Activity 生命周期处理 |
示例代码:添加 ViewModel 支持
在 build.gradle (Module: app) 中添加以下依赖:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
同步 Gradle 后,即可在项目中使用 ViewModel 和 LiveData。
2.3 开发环境常见问题与解决方案
在开发过程中,开发者常常会遇到环境配置问题,以下是一些常见问题及其解决方法。
2.3.1 启动失败与SDK路径问题
问题描述:
Android Studio 启动时报错,提示找不到 SDK 或路径错误。
解决方案:
- 打开 File > Settings > Appearance & Behavior > System Settings 。
- 检查 SDK 路径是否正确。
- 如果路径错误,重新指定 SDK 路径。
- 若 SDK 损坏,可通过 SDK Manager 重新下载。
示例命令:手动检查 SDK 状态
# 进入 SDK tools 目录
cd $ANDROID_SDK_ROOT/tools/bin
# 列出可用镜像
sdkmanager --list
# 更新 SDK
sdkmanager --update
2.3.2 Gradle同步错误与依赖冲突处理
问题描述:
Gradle 同步失败,提示依赖冲突或版本不匹配。
解决方案:
- 查看报错信息中的依赖名称和版本。
- 在
build.gradle (Module: app)中统一版本号。 - 使用
implementation替代compile(已弃用)。 - 使用
--stacktrace或--info查看详细错误。
示例代码:解决依赖冲突
configurations.all {
resolutionStrategy {
force 'androidx.appcompat:appcompat:1.6.1'
force 'androidx.core:core-ktx:1.9.0'
}
}
流程图:Gradle 构建流程
graph TD
A[开始构建] --> B[解析 build.gradle 文件]
B --> C[下载依赖]
C --> D[编译源码]
D --> E[打包 APK]
E --> F[部署到设备]
F --> G[运行应用]
通过本章内容,我们完成了 Android 开发环境的搭建、五子棋项目的创建与基础结构配置,并探讨了开发过程中常见的问题及其解决方法。接下来的章节中,我们将深入探讨五子棋游戏界面的设计与交互实现。
3. 五子棋游戏界面设计与用户交互实现
在五子棋游戏的开发过程中,界面设计和用户交互是决定用户体验的关键因素。一个清晰、直观的界面不仅能让用户快速理解游戏规则,还能增强游戏的可玩性。本章将从游戏主界面布局设计入手,逐步深入讲解如何通过XML和自定义View实现棋盘绘制、如何绑定触摸事件与落子逻辑,以及如何增强用户交互体验,包括悔棋、重置和状态提示功能的实现。
3.1 游戏主界面布局设计
3.1.1 使用XML实现棋盘网格布局
Android界面布局通常通过XML文件进行描述。为了实现五子棋的棋盘网格,我们可以使用 GridLayout 或 LinearLayout 来模拟15x15的棋盘格。
<!-- res/layout/activity_main.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<GridLayout
android:id="@+id/chess_board"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:columnCount="15"
android:rowCount="15"
android:background="@drawable/board_background">
</GridLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:paddingTop="16dp">
<Button
android:id="@+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重置" />
<Button
android:id="@+id/btn_undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="悔棋" />
</LinearLayout>
<TextView
android:id="@+id/tv_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="游戏进行中"
android:textSize="18sp"
android:layout_marginTop="16dp"/>
</LinearLayout>
代码逻辑分析:
- 使用
LinearLayout作为根布局,保证上下结构清晰; - 使用
GridLayout来模拟15x15的棋盘,每个格子将放置一个棋子(ImageButton或ImageView); - 添加重置和悔棋按钮,方便用户操作;
- 添加状态TextView,用于显示当前游戏状态(如“黑方胜利”、“白方胜利”、“平局”等);
- 设置
layout_weight属性使棋盘占据主要空间,保持界面比例合理; - 棋盘背景通过
board_background资源设置,可为棋盘增加视觉美感。
参数说明:
android:columnCount和android:rowCount:设置棋盘的行列数;android:layout_weight:控制布局权重,实现高度自适应;android:gravity:控制子控件的对齐方式。
3.1.2 自定义View绘制棋盘与棋子
为了实现更灵活的棋盘绘制和动画效果,我们可以通过继承 View 类,自定义一个 ChessBoardView ,并在其中绘制棋盘和棋子。
class ChessBoardView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val paint = Paint()
private val blackPiece = BitmapFactory.decodeResource(resources, R.drawable.black_piece)
private val whitePiece = BitmapFactory.decodeResource(resources, R.drawable.white_piece)
private val boardSize = 15 // 棋盘大小
private val cellSize = 0f // 每个格子的大小
private val pieces = Array(boardSize) { Array<Bitmap?>(boardSize) { null } }
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
cellSize = w / boardSize.toFloat()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 绘制棋盘线
paint.color = Color.BLACK
paint.strokeWidth = 2f
for (i in 0 until boardSize) {
canvas.drawLine(i * cellSize, 0f, i * cellSize, height.toFloat(), paint)
canvas.drawLine(0f, i * cellSize, width.toFloat(), i * cellSize, paint)
}
// 绘制棋子
for (i in 0 until boardSize) {
for (j in 0 until boardSize) {
pieces[i][j]?.let { bitmap ->
val x = i * cellSize
val y = j * cellSize
canvas.drawBitmap(bitmap, x - bitmap.width / 2, y - bitmap.height / 2, null)
}
}
}
}
fun placePiece(row: Int, col: Int, isBlack: Boolean) {
pieces[row][col] = if (isBlack) blackPiece else whitePiece
invalidate()
}
fun clearBoard() {
for (i in 0 until boardSize) {
for (j in 0 until boardSize) {
pieces[i][j] = null
}
}
invalidate()
}
}
代码逻辑分析:
onSizeChanged中计算每个格子的大小cellSize,确保棋盘适配不同屏幕;onDraw方法中绘制棋盘的网格线和已落子的棋子;placePiece方法用于在指定位置绘制棋子;clearBoard用于清空棋盘;- 使用
BitmapFactory加载棋子图片资源,避免内存浪费。
参数说明:
boardSize:棋盘大小(15x15);cellSize:每个格子的实际像素大小;pieces:二维数组用于记录棋子的位置;paint:绘制棋盘的画笔对象;invalidate():触发View的重绘,确保界面更新。
3.1.3 使用ConstraintLayout实现响应式界面
为了适配不同屏幕尺寸并实现更灵活的布局,我们可以使用 ConstraintLayout 替代 LinearLayout 和 GridLayout ,提高界面的响应性。
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.gomoku.ChessBoardView
android:id="@+id/chessBoardView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/btnLayout"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
<LinearLayout
android:id="@+id/btnLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<Button
android:id="@+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重置" />
<Button
android:id="@+id/btn_undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="悔棋" />
</LinearLayout>
<TextView
android:id="@+id/tv_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="游戏进行中"
android:textSize="18sp"
app:layout_constraintTop_toBottomOf="@id/chessBoardView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/btnLayout"
app:layout_constraintVertical_chainStyle="packed"/>
</androidx.constraintlayout.widget.ConstraintLayout>
逻辑分析:
ConstraintLayout通过约束链实现棋盘、按钮和状态文本的垂直分布;- 棋盘
ChessBoardView通过约束上下左右来自动填充可用空间; - 按钮和状态信息通过约束链实现垂直居中排列;
- 这种方式比
LinearLayout更灵活,适配性更强。
3.2 触摸事件监听与落子逻辑绑定
3.2.1 实现OnTouchListener监听用户点击
用户通过点击屏幕来落子,因此需要为 ChessBoardView 添加 OnTouchListener ,监听用户的触摸事件。
chessBoardView.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
val row = (event.y / cellSize).toInt()
val col = (event.x / cellSize).toInt()
// 调用落子逻辑
placePiece(row, col)
}
true
}
逻辑分析:
- 通过
MotionEvent.ACTION_DOWN判断用户点击动作; - 计算点击位置对应的行和列;
- 调用
placePiece方法完成落子操作; - 返回
true表示事件已被消费。
参数说明:
event.x和event.y:获取点击的坐标;cellSize:每个格子的像素大小;row和col:对应棋盘的行列索引。
3.2.2 将坐标转换为棋盘行列索引
在触摸事件中获取的坐标是屏幕像素坐标,需要将其转换为棋盘的行列索引:
val row = (event.y / cellSize).toInt()
val col = (event.x / cellSize).toInt()
逻辑分析:
- 用点击坐标除以每个格子的大小,得到行和列索引;
- 由于
cellSize是浮点数,除法结果需使用toInt()转换为整数; - 注意边界处理,避免超出棋盘范围。
3.2.3 落子动画与音效反馈机制
为了增强用户交互体验,可以在落子时添加动画和音效。
private var soundPool: SoundPool? = null
private var blackSoundId = 0
private var whiteSoundId = 0
private fun initSound(context: Context) {
soundPool = SoundPool.Builder().setMaxStreams(2).build().apply {
blackSoundId = load(context, R.raw.black_piece_sound, 1)
whiteSoundId = load(context, R.raw.white_piece_sound, 1)
}
}
private fun playSound(isBlack: Boolean) {
soundPool?.play(
if (isBlack) blackSoundId else whiteSoundId,
1f, 1f, 0, 0, 1f
)
}
动画实现示例:
val animator = ObjectAnimator.ofFloat(pieceView, "scaleX", 0f, 1f).setDuration(200)
ObjectAnimator.ofFloat(pieceView, "scaleY", 0f, 1f).setDuration(200).start()
animator.start()
逻辑分析:
- 使用
SoundPool加载音频资源,播放落子音效; - 动画通过
ObjectAnimator实现棋子放大效果; - 提升用户反馈,增强沉浸感。
3.3 用户交互增强功能实现
3.3.1 悔棋功能的实现与棋盘状态回退
悔棋功能需要记录每次落子的历史记录,并提供回退功能。
data class Move(val row: Int, val col: Int, val isBlack: Boolean)
private val moveHistory = Stack<Move>()
fun placePiece(row: Int, col: Int, isBlack: Boolean) {
// 记录历史
moveHistory.push(Move(row, col, isBlack))
pieces[row][col] = if (isBlack) blackPiece else whitePiece
invalidate()
}
fun undoLastMove() {
if (moveHistory.isNotEmpty()) {
val lastMove = moveHistory.pop()
pieces[lastMove.row][lastMove.col] = null
invalidate()
}
}
逻辑分析:
- 使用
Stack保存每次落子的动作; undoLastMove弹出栈顶动作,清空对应棋子;invalidate()触发界面重绘,实现悔棋效果。
3.3.2 重置按钮的点击事件与数据清空
重置按钮用于清空棋盘和所有历史记录。
btn_reset.setOnClickListener {
chessBoardView.clearBoard()
moveHistory.clear()
tv_status.text = "游戏进行中"
}
逻辑分析:
- 调用
clearBoard()清空棋子; - 清空
moveHistory历史记录; - 重置状态文本。
3.3.3 提示信息与游戏状态显示设计
游戏状态提示信息通过 TextView 实时更新。
fun updateGameStatus(status: String) {
tv_status.text = status
}
状态示例:
- “黑方胜利”
- “白方胜利”
- “平局”
- “轮到黑方”
- “轮到白方”
mermaid流程图:
graph TD
A[用户点击棋盘] --> B[获取坐标]
B --> C[转换为行列]
C --> D[判断是否合法]
D -- 合法 --> E[落子]
E --> F[更新状态]
F --> G{胜负判断}
G -- 是 --> H[显示胜利信息]
G -- 否 --> I[切换玩家]
表格:游戏状态码对照表
| 状态码 | 含义 |
|---|---|
| 0 | 游戏进行中 |
| 1 | 黑方胜利 |
| 2 | 白方胜利 |
| 3 | 平局 |
| 4 | 轮到黑方 |
| 5 | 轮到白方 |
本章从界面布局设计、自定义View绘制、触摸事件绑定,到交互增强功能(悔棋、重置、状态提示)进行了详尽讲解,并结合代码、表格和流程图展示了实现细节。通过本章内容,读者可以掌握Android五子棋游戏界面设计与交互实现的核心技术,为后续游戏逻辑开发打下坚实基础。
4. 五子棋核心逻辑与胜负判断算法设计
本章深入讲解五子棋游戏的核心逻辑实现,包括棋盘数据结构的设计与实现、胜负判断算法的编写以及游戏逻辑的模块化封装。通过本章内容,开发者将掌握如何高效管理棋盘状态、实现准确的胜负判断逻辑,并通过模块化设计提升代码的可维护性和可扩展性。这些内容对于中高级Android开发者来说,将有助于构建更复杂的游戏逻辑体系。
4.1 棋盘数据结构设计与实现
4.1.1 使用二维数组表示棋盘状态
在五子棋游戏中,棋盘通常为15x15的网格,每一格可以是空位、黑子或白子。为高效表示棋盘状态,最常用的数据结构是二维数组。
public class ChessBoard {
private static final int EMPTY = 0;
private static final int BLACK = 1;
private static final int WHITE = 2;
private int[][] board;
public ChessBoard(int size) {
board = new int[size][size];
resetBoard();
}
public void resetBoard() {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
board[i][j] = EMPTY;
}
}
}
public int getCell(int row, int col) {
return board[row][col];
}
public boolean setCell(int row, int col, int player) {
if (board[row][col] == EMPTY) {
board[row][col] = player;
return true;
}
return false;
}
}
代码解释:
- EMPTY 、 BLACK 、 WHITE 三个常量分别表示空位、黑子、白子;
- board 为二维数组,表示棋盘上每个位置的状态;
- resetBoard() 方法用于初始化或重置棋盘;
- setCell() 方法用于设置某一位置的棋子,并确保该位置为空。
逻辑分析:
二维数组结构简单、访问高效,适合用于棋盘状态管理。每个位置的值表示当前是否被占据及由哪一方占据,便于后续的胜负判断逻辑实现。
4.1.2 数据结构的初始化与更新策略
在游戏初始化阶段,需要对棋盘进行初始化操作。更新策略则是在每次落子后同步更新棋盘状态。
public class GameViewModel {
private ChessBoard chessBoard;
private int currentPlayer;
public GameViewModel() {
chessBoard = new ChessBoard(15);
currentPlayer = ChessBoard.BLACK;
}
public boolean placePiece(int row, int col) {
if (chessBoard.setCell(row, col, currentPlayer)) {
switchPlayer();
return true;
}
return false;
}
private void switchPlayer() {
currentPlayer = (currentPlayer == ChessBoard.BLACK) ? ChessBoard.WHITE : ChessBoard.BLACK;
}
public int getCurrentPlayer() {
return currentPlayer;
}
public void resetGame() {
chessBoard.resetBoard();
currentPlayer = ChessBoard.BLACK;
}
}
代码解释:
- GameViewModel 类封装了游戏逻辑和棋盘数据;
- placePiece() 方法用于在指定位置落子,并切换玩家;
- resetGame() 方法用于重置游戏状态。
更新策略分析:
每次落子成功后,更新棋盘状态并切换玩家,保证游戏流程的正确执行。初始化和重置逻辑保持一致,便于在悔棋或重新开始时快速恢复初始状态。
4.1.3 棋盘数据的持久化与恢复
为了实现悔棋功能或保存游戏进度,可以将棋盘数据进行持久化处理,例如保存为文件或数据库。
public void saveGameState(Context context, int[][] boardState) {
try {
FileOutputStream fos = context.openFileOutput("game_state.dat", Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(boardState);
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public int[][] loadGameState(Context context) {
try {
FileInputStream fis = context.openFileInput("game_state.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
int[][] boardState = (int[][]) ois.readObject();
ois.close();
fis.close();
return boardState;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
代码解释:
- saveGameState() 方法将棋盘状态保存为本地文件;
- loadGameState() 方法从文件中恢复棋盘状态;
- 使用 ObjectOutputStream 和 ObjectInputStream 实现对象序列化和反序列化。
持久化策略分析:
通过序列化方式保存棋盘数据,可以实现游戏状态的完整恢复。适用于悔棋、退出重进等场景,增强用户体验。
表格:棋盘数据结构比较
| 数据结构 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 二维数组 | 简单高效,易于访问 | 不支持动态扩展 | 固定大小棋盘(如15x15) |
| SparseArray | 内存效率高 | 实现复杂,访问效率略低 | 大棋盘、稀疏数据 |
| List > | 支持动态扩展 | 性能略差 | 需要动态调整棋盘尺寸 |
4.2 游戏规则与胜负判断算法
4.2.1 判断五子连珠的逻辑分析(横向、纵向、斜向)
胜负判断的核心逻辑是判断某一位置是否形成“五子连珠”。需要检查四个方向:横向、纵向、左斜向、右斜向。
public boolean checkWin(int row, int col, int player) {
return checkDirection(row, col, player, 1, 0) || // 横向
checkDirection(row, col, player, 0, 1) || // 纵向
checkDirection(row, col, player, 1, 1) || // 左斜向
checkDirection(row, col, player, 1, -1); // 右斜向
}
private boolean checkDirection(int row, int col, int player, int dx, int dy) {
int count = 1;
count += countInDirection(row, col, player, dx, dy);
count += countInDirection(row, col, player, -dx, -dy);
return count >= 5;
}
private int countInDirection(int row, int col, int player, int dx, int dy) {
int count = 0;
int r = row + dx;
int c = col + dy;
while (r >= 0 && r < 15 && c >= 0 && c < 15 && board[r][c] == player) {
count++;
r += dx;
c += dy;
}
return count;
}
代码解释:
- checkWin() 方法用于判断当前落子是否导致胜利;
- checkDirection() 方法检查某个方向上的连续棋子数量;
- countInDirection() 方法用于在某一方向上统计连续棋子数。
逻辑分析:
每次落子后,从该位置出发,在四个方向上分别统计连续的同色棋子数量,若任一方向上达到5个,则判定胜利。
4.2.2 使用方向向量优化判断效率
使用方向向量可以统一四个方向的判断逻辑,提高代码可读性和效率。
private final int[][] DIRECTIONS = {
{1, 0}, // 横向
{0, 1}, // 纵向
{1, 1}, // 左斜向
{1, -1} // 右斜向
};
public boolean checkWin(int row, int col, int player) {
for (int[] dir : DIRECTIONS) {
int count = 1;
count += countInDirection(row, col, player, dir[0], dir[1]);
count += countInDirection(row, col, player, -dir[0], -dir[1]);
if (count >= 5) return true;
}
return false;
}
代码解释:
- DIRECTIONS 数组保存四个方向的向量;
- checkWin() 方法遍历所有方向,统一调用 countInDirection() 方法进行统计。
效率优化分析:
使用方向向量统一逻辑,避免重复代码,也便于后续扩展(如增加新方向)。
4.2.3 多人对战与AI判断逻辑的统一处理
在多人对战和AI对战中,胜负判断逻辑是相同的。因此,可以将胜负判断逻辑封装为独立方法,供不同模式调用。
public interface GameListener {
void onGameWin(int winner);
void onGameDraw();
}
public class GameController {
private GameViewModel viewModel;
private GameListener listener;
public void handleMove(int row, int col) {
if (viewModel.placePiece(row, col)) {
if (viewModel.checkWin(row, col)) {
listener.onGameWin(viewModel.getCurrentPlayer());
} else if (viewModel.isBoardFull()) {
listener.onGameDraw();
}
}
}
}
代码解释:
- GameListener 接口用于通知游戏状态变化;
- GameController 类统一处理玩家落子、胜负判断和游戏状态通知。
交互设计分析:
通过接口回调机制,将胜负判断结果通知到UI层或AI逻辑层,实现逻辑与UI的解耦,提高代码复用性。
mermaid流程图:胜负判断流程
graph TD
A[落子] --> B{是否形成五子连珠}
B -- 是 --> C[通知胜利]
B -- 否 --> D{是否棋盘已满}
D -- 是 --> E[通知平局]
D -- 否 --> F[继续游戏]
4.3 游戏逻辑的封装与模块化设计
4.3.1 将游戏逻辑封装为独立类或ViewModel
为提高代码可维护性和复用性,将游戏逻辑封装到 ViewModel 中,使用 LiveData 实现数据驱动的UI更新。
public class GameViewModel extends AndroidViewModel {
private MutableLiveData<int[][]> boardLiveData;
private MutableLiveData<Integer> currentPlayerLiveData;
private ChessBoard chessBoard;
public GameViewModel(@NonNull Application application) {
super(application);
chessBoard = new ChessBoard(15);
boardLiveData = new MutableLiveData<>();
currentPlayerLiveData = new MutableLiveData<>(ChessBoard.BLACK);
}
public void placePiece(int row, int col) {
if (chessBoard.setCell(row, col, currentPlayerLiveData.getValue())) {
if (chessBoard.checkWin(row, col, currentPlayerLiveData.getValue())) {
// 触发胜利状态
}
switchPlayer();
boardLiveData.setValue(chessBoard.getBoard());
}
}
public LiveData<int[][]> getBoardLiveData() {
return boardLiveData;
}
public LiveData<Integer> getCurrentPlayerLiveData() {
return currentPlayerLiveData;
}
private void switchPlayer() {
currentPlayerLiveData.setValue(
(currentPlayerLiveData.getValue() == ChessBoard.BLACK) ? ChessBoard.WHITE : ChessBoard.BLACK
);
}
}
代码解释:
- GameViewModel 继承 AndroidViewModel ,用于生命周期感知;
- 使用 LiveData 实现棋盘和玩家状态的实时更新;
- placePiece() 方法触发落子、胜负判断和玩家切换。
封装逻辑分析:
通过 ViewModel 和 LiveData 的组合,实现数据与UI的分离,使得游戏逻辑更加清晰、可测试性强。
4.3.2 使用LiveData实现数据驱动UI更新
在UI层观察 LiveData ,自动更新界面状态。
public class GameActivity extends AppCompatActivity {
private GameViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
viewModel = new ViewModelProvider(this).get(GameViewModel.class);
viewModel.getBoardLiveData().observe(this, board -> {
// 更新棋盘UI
});
viewModel.getCurrentPlayerLiveData().observe(this, player -> {
// 更新当前玩家提示
});
}
public void onCellClick(int row, int col) {
viewModel.placePiece(row, col);
}
}
代码解释:
- 在 GameActivity 中观察 LiveData ,实现自动更新;
- 点击事件调用 ViewModel 的落子方法。
数据驱动优势:
通过 LiveData ,UI层无需手动管理状态更新,提升开发效率和稳定性。
4.3.3 游戏逻辑与UI层的解耦实践
将游戏逻辑与UI分离,提高代码可维护性和可测试性。
public class GameLogic {
private ChessBoard chessBoard;
private int currentPlayer;
public GameLogic() {
chessBoard = new ChessBoard(15);
currentPlayer = ChessBoard.BLACK;
}
public boolean placePiece(int row, int col) {
if (chessBoard.setCell(row, col, currentPlayer)) {
if (checkWin(row, col)) {
// 触发胜利
}
switchPlayer();
return true;
}
return false;
}
public int[][] getBoardState() {
return chessBoard.getBoard();
}
public int getCurrentPlayer() {
return currentPlayer;
}
private void switchPlayer() {
currentPlayer = (currentPlayer == ChessBoard.BLACK) ? ChessBoard.WHITE : ChessBoard.BLACK;
}
public boolean checkWin(int row, int col) {
return chessBoard.checkWin(row, col, currentPlayer);
}
}
代码解释:
- GameLogic 类封装了完整的游戏逻辑;
- 提供获取棋盘状态和当前玩家的方法;
- 所有胜负判断逻辑在内部完成。
解耦设计分析:
将游戏逻辑完全封装在 GameLogic 类中,UI层仅负责展示和事件处理,实现高内聚、低耦合的架构设计。
通过本章的深入讲解,开发者可以全面掌握五子棋游戏的核心逻辑实现,包括棋盘数据结构的设计、胜负判断算法的编写以及模块化封装策略。这些内容不仅适用于五子棋开发,也适用于其他棋类或策略类游戏的开发实践。
5. Android应用发布与实战优化总结
本章聚焦于Android应用的最终发布流程与实战优化策略。通过前几章的开发,五子棋游戏的核心功能已基本实现,包括落子、悔棋、胜负判断等。本章将围绕应用的发布准备、兼容性处理、构建流程以及后期优化方向展开,帮助开发者顺利完成从开发到上线的全过程。
5.1 移动端适配与兼容性处理
5.1.1 多种屏幕尺寸的响应式布局适配
在Android开发中,适配不同屏幕尺寸是确保用户体验一致性的关键环节。以下是几种常用的适配策略:
- 使用ConstraintLayout :通过约束布局实现灵活的控件定位,适应不同分辨率。
- 资源目录限定符 :如
layout-sw600dp、drawable-xhdpi等,为不同设备提供专属资源。 - 使用dp与sp单位 :避免使用px,确保在不同密度屏幕上显示一致。
<!-- 示例:使用ConstraintLayout实现适配布局 -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/chess_board"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.9"
app:layout_constraintHeight_percent="0.9"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
5.1.2 不同Android版本的兼容性处理
从Android 6.0(API 23)开始,系统引入了运行时权限机制,开发者需特别注意以下几点:
- 使用
ContextCompat.checkSelfPermission()检查权限状态。 - 使用
ActivityCompat.requestPermissions()请求权限。 - 针对Android 10以上版本,需适配Scoped Storage。
// 请求存储权限示例
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
5.1.3 横竖屏切换与状态保存
为防止横竖屏切换导致界面重绘和数据丢失,可通过以下方式处理:
- 在
AndroidManifest.xml中为Activity添加android:configChanges="orientation|screenSize"。 - 覆写
onConfigurationChanged()方法。 - 使用
ViewModel或onSaveInstanceState()保存临时状态。
// 使用ViewModel保存棋盘状态
class GameViewModel : ViewModel() {
val boardState = MutableLiveData<Array<Array<Int>>>()
}
5.2 APK构建与发布流程
5.2.1 配置签名文件(Keystore)生成
为了发布正式版本,必须生成签名密钥文件(Keystore)。可通过Android Studio的 Build > Generate Signed Bundle / APK 功能完成:
- 选择“Create New”创建新的Keystore。
- 填写别名(Key Alias)、密码、有效期等信息。
- 保存keystore文件并妥善保管。
生成后的配置如下所示:
storeFile=release.keystore
storePassword=your_store_password
keyAlias=your_key_alias
keyPassword=your_key_password
5.2.2 使用Gradle构建Release版本
在 build.gradle 中配置签名配置,并构建Release版本:
android {
...
signingConfigs {
release {
storeFile file("release.keystore")
storePassword "your_store_password"
keyAlias "your_key_alias"
keyPassword "your_key_password"
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
使用以下命令构建APK:
./gradlew assembleRelease
构建完成后,APK文件位于 app/release/ 目录下。
5.2.3 发布到Google Play或国内应用市场
- Google Play :注册开发者账号(25美元),上传APK文件,填写应用信息、截图、隐私政策等。
- 国内应用市场 :如应用宝、华为应用市场、小米应用商店等,需分别注册账号并提交审核。
发布前需注意:
- 应用图标与截图需符合各平台规范。
- 提供应用简介、更新日志、联系方式。
- 确保无敏感权限滥用,避免审核驳回。
5.3 项目总结与后续优化方向
5.3.1 五子棋游戏开发过程中的经验总结
通过本项目的开发,我们掌握了以下关键技术点:
- Android界面布局与自定义View绘制。
- 触摸事件监听与坐标转换。
- 游戏逻辑封装与数据绑定(ViewModel + LiveData)。
- 多版本兼容与适配策略。
项目中遇到的问题如:
- 横竖屏切换导致的棋盘状态丢失。
- 多点触控导致的落子冲突。
- 胜负判断算法的性能瓶颈。
这些问题的解决提升了对Android平台特性的理解。
5.3.2 可扩展功能:AI对战、网络联机、计时系统
为进一步提升游戏体验,可考虑以下功能扩展:
- AI对战 :引入Minimax算法或Alpha-Beta剪枝实现智能AI。
- 网络联机 :使用WebSocket或Firebase实现在线对战。
- 计时系统 :为每位玩家添加倒计时,提升竞技性。
例如,添加计时功能可使用 CountDownTimer :
new CountDownTimer(60000, 1000) {
public void onTick(long millisUntilFinished) {
textView.setText("剩余时间: " + millisUntilFinished / 1000);
}
public void onFinish() {
textView.setText("时间到!");
}
}.start();
5.3.3 Android开发中常见的性能优化与代码规范建议
- 性能优化 :
- 减少主线程耗时操作,使用
Handler或AsyncTask。 - 避免内存泄漏,使用弱引用或LeakCanary检测。
-
图片资源使用Glide或Picasso加载,避免OOM。
-
代码规范建议 :
- 使用Kotlin替代Java,提升代码简洁性。
- 遵循MVC/MVVM架构,保持职责分离。
- 使用Lint工具检查代码质量,统一命名规范。
graph TD
A[项目开发] --> B[UI设计]
A --> C[核心逻辑]
A --> D[数据绑定]
B --> E[响应式布局]
C --> F[胜负判断算法]
D --> G[ViewModel + LiveData]
E --> H[适配多分辨率]
F --> I[方向向量优化]
G --> J[数据驱动UI]
H --> K[ConstraintLayout]
I --> L[横向纵向斜向扫描]
J --> M[实时更新棋子]
通过以上优化与扩展,五子棋游戏将更具完整性和可维护性,同时也为后续Android项目开发提供了良好的实践基础。
简介:本项目是一款基于Android平台的五子棋小游戏,使用Android Studio开发,结合Java或Kotlin语言实现。应用包含完整的游戏逻辑与交互功能,支持两人对战模式,具备良好的用户界面和响应式布局。项目涵盖Android开发的多个核心技术点,包括界面设计、触控事件处理、胜负判断算法、构建与发布流程,适合用于掌握移动端游戏开发全流程的实战学习。
更多推荐


所有评论(0)