简介:这份资源是面向高校学生与Java初学者的一套Android课程设计完整源码,基于Java语言在Android Studio环境下实现经典井字棋小游戏,适合用作课程大作业参考、移动开发入门练手或答辩演示项目。压缩包共65个文件,约248KB,以28个xml布局与配置、7个java业务逻辑类、10个webp与6个png图片资源为主,另含gradle构建脚本、properties配置、jar依赖及proguard混淆规则等,工程结构完整,可直接导入Android Studio编译运行。内容涵盖棋盘绘制、落子判定、胜负检测与界面交互等核心模块,目录层次清晰,便于读者理解Android项目从Gradle配置到Activity逻辑的完整组织方式,也可在此基础上扩展人机对战、计分统计等功能。目前已有3035人学习下载,适合需要快速获取可运行课程设计源码、对照学习Android开发流程的读者参考使用。
1. 从一份能跑通的井字棋源码说起:它到底能帮你省下多少时间
课程设计选题定下来那一刻,很多人第一反应不是写代码,而是打开搜索引擎找一份能跑的 Android 井字棋小游戏源码。原因很现实:从零搭一个 Android Studio 工程、配 Gradle、写布局、调逻辑,光是环境跑通就能耗掉两三天,而真正要交的是能演示、能讲清逻辑、能改出自己东西的成品。这份基于 Java 的 Android Studio 井字棋小游戏源码,正好卡在这个需求点上——它是一个标准的单模块 Android 工程,用 Java 写业务逻辑,用 XML 做界面,棋盘是 3×3 的九宫格,双方轮流落子,判定横竖斜三连。适合谁?适合课程设计周期紧、需要一份结构清晰可二次开发底稿的人,也适合刚接触 Android 想拿一个完整小工程练手的人。它不炫技,但胜在工程结构完整、依赖干净、逻辑一眼能看懂,改起来不费劲。
2. 拆开工程看结构:Gradle 配置、源码目录与资源文件怎么对应
拿到压缩包解压后,第一件事不是急着点运行,而是先看清目录。这份源码的根目录是TicTacToeGame-master,里面是典型的 Android Studio 工程骨架。很多人翻车就翻在没看结构直接导入,结果 Gradle 版本对不上、SDK 路径找不到,白白折腾半天。先把结构摸清楚,后面每一步都省事。
2.1 根目录与模块目录的分工
根目录下能看到这些关键项:.gitignore、gradle wrapper、build.gradle、gradlew、gradlew.bat、settings.gradle、gradle.properties,以及.idea目录。app目录才是真正的应用模块,里面又有自己的.gitignore、build.gradle、proguard-rules.pro和src。
这里要分清两层build.gradle:根目录那个管的是整个工程的构建配置,比如插件版本、仓库地址;app/build.gradle管的是应用模块自己的编译参数,比如compileSdk、minSdk、targetSdk、依赖库。新手最容易搞混,改错了地方,编译报错还找不到原因。
gradle wrapper目录和gradlew、gradlew.bat是配套的。gradlew是 Linux/Mac 用的脚本,gradlew.bat是 Windows 用的。它们的作用是让工程用指定版本的 Gradle 构建,而不是依赖你本机装了什么版本。这一点对课程设计特别重要——你换台电脑演示,只要带着 wrapper,构建环境基本一致。
settings.gradle里通常只有一行include ':app',声明工程包含哪些模块。gradle.properties放的是全局属性,比如 JVM 内存参数、AndroidX 开关。.idea是 IDE 的本地配置,一般不用手动改,也不建议提交到版本库。
2.2 src 目录下的代码与资源分布
app/src是核心,标准结构是main/java、main/res、main/AndroidManifest.xml。Java 代码放在main/java/包名/下,井字棋的逻辑类、Activity 类都在这里。main/res下分layout(布局 XML)、values(字符串、颜色、主题)、drawable(图片资源)、mipmap(应用图标)。
判断一份 Android 源码是否规范,看res目录就能看出七八成。如果字符串硬编码在布局里、颜色值到处散落,后期改主题会很痛苦。这份源码的values目录里应该有strings.xml、colors.xml、themes.xml,把文案和配色抽出来,改起来只动一处。
AndroidManifest.xml是应用的入口声明,里面注册 Activity、声明权限、设置应用图标和主题。井字棋这种小游戏一般不需要额外权限,如果看到申请了网络、存储之类无关权限,那就要留个心眼。
2.3 用一条命令确认工程完整性
导入之前,我习惯先在终端里跑一遍 Gradle 任务列表,确认 wrapper 能正常工作。进入工程根目录,执行:
# Windows 用 gradlew.bat,Linux/Mac 用 ./gradlew ./gradlew tasks --all这条命令会列出当前工程所有可执行任务。如果能看到assembleDebug、installDebug、clean这些任务,说明 Gradle 配置基本没问题。如果报错说找不到 Gradle 或者版本不兼容,那就要先处理 wrapper 里的版本声明。
gradle wrapper目录下有个gradle-wrapper.properties,里面distributionUrl指定了 Gradle 版本。常见坑是这里写的版本太老,和新版 Android Studio 不兼容。解决办法是把distributionUrl改成你本机 Android Studio 支持的版本,或者直接用 IDE 的「Sync Project with Gradle Files」让它自动处理。
提示:改
gradle-wrapper.properties之前先备份,改完执行一次./gradlew clean再同步,避免缓存干扰。
3. 导入 Android Studio 并跑起来:SDK、Gradle 与模拟器三步走
结构看清了,接下来就是把它跑起来。这一步的目标很明确:让工程在 Android Studio 里编译通过,并在模拟器或真机上看到九宫格界面。整个过程分三步——配 SDK、同步 Gradle、选设备运行。每一步都有对应的检查点,卡住了就按检查点排查。
3.1 配置 SDK 与编译参数
打开 Android Studio,选「Open」而不是「New Project」,定位到解压后的TicTacToeGame-master目录。打开后 IDE 会自动开始同步,但同步之前最好先确认app/build.gradle里的 SDK 版本。
android { compileSdk 33 // 编译用的 SDK 版本 defaultConfig { applicationId "com.example.tictactoe" minSdk 21 // 最低支持的 Android 版本 targetSdk 33 // 目标适配版本 versionCode 1 versionName "1.0" } }compileSdk决定用哪个版本的 Android SDK 编译,minSdk决定应用能装在多老的设备上,targetSdk决定系统按哪个版本的行为来适配你的应用。井字棋这种纯本地小游戏,minSdk设 21(Android 5.0)足够覆盖绝大多数设备。如果compileSdk写的版本你本机没装,Android Studio 会提示下载,点同意即可。
applicationId是应用包名,课程设计如果要求特定包名,在这里改。改完记得同步一次。
3.2 同步 Gradle 与处理依赖
SDK 确认后,点工具栏的「Sync Project with Gradle Files」。同步过程会下载依赖、编译配置。如果卡在「Download」很久,多半是仓库地址访问慢,可以在根目录build.gradle里确认仓库配置:
allprojects { repositories { google() mavenCentral() } }google()和mavenCentral()是标准仓库。同步失败时,先看 Build 窗口的报错信息,常见的有三类:SDK 版本找不到、依赖库版本冲突、Gradle 版本不兼容。SDK 问题去 SDK Manager 装对应版本;依赖冲突看具体是哪个库,调整版本号;Gradle 版本问题改gradle-wrapper.properties。
同步成功后,Build 窗口会显示「BUILD SUCCESSFUL」。这时候工程已经具备编译条件了。
3.3 选择设备并运行
点工具栏的绿色三角运行按钮,IDE 会让你选设备。没有真机就创建模拟器:Tools → Device Manager → Create Device,选一个手机型号,再选系统镜像。井字棋对性能没要求,随便一个 API 21 以上的镜像都能跑。
运行后,应用会安装到设备上并自动启动。你应该能看到一个 3×3 的九宫格,点击格子会落子,双方轮流,三连后弹出胜负提示。如果界面空白或者闪退,看 Logcat 的报错。闪退最常见的原因是布局文件里引用了不存在的资源 ID,或者 Activity 没在AndroidManifest.xml里注册。
<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>上面这段是启动 Activity 的标准注册方式。MAIN和LAUNCHER两个 intent-filter 缺一不可,少了应用就没有入口图标。
注意:模拟器首次启动较慢,如果卡在开机动画超过五分钟,检查电脑是否开启了硬件加速(HAXM 或 Hyper-V)。
4. 井字棋核心逻辑怎么读:棋盘状态、胜负判定与落子流程
跑起来只是第一步,课程设计要讲清逻辑,就得把核心代码读透。井字棋看着简单,但胜负判定和状态管理里有几个容易写错的地方。这一章把棋盘表示、落子流程、胜负判定三块拆开讲,读完你能自己改规则、加功能。
4.1 棋盘状态用什么数据结构
3×3 棋盘最常见的表示是一个长度为 9 的一维数组,或者 3×3 的二维数组。一维数组写起来更紧凑,索引 0 到 8 对应九个格子,从左到右、从上到下排列。
// 0 表示空,1 表示玩家 X,2 表示玩家 O private int[] board = new int[9]; private int currentPlayer = 1; // 当前轮到谁用int而不是boolean或char,好处是扩展方便——以后想加「悔棋」功能,记录每一步的索引和玩家即可。currentPlayer记录当前轮到谁,每次落子后切换。
棋盘状态和 UI 是两回事。UI 上的九宫格按钮只是显示层,真正的状态在board数组里。点击按钮时,先更新数组,再刷新按钮显示。这种「数据驱动 UI」的思路是 Android 开发的基本功,别把状态存在按钮的 tag 里,那样逻辑会乱。
4.2 落子与胜负判定的实现
落子的核心逻辑是:判断格子是否为空,为空则写入当前玩家,切换玩家,然后检查胜负。
public void onCellClicked(int index) { if (board[index] != 0) return; // 格子已占用,忽略 board[index] = currentPlayer; // 落子 if (checkWin(currentPlayer)) { showResult(currentPlayer + " 获胜"); return; } if (isBoardFull()) { showResult("平局"); return; } currentPlayer = (currentPlayer == 1) ? 2 : 1; // 切换玩家 }checkWin是胜负判定的关键。3×3 棋盘只有 8 种获胜组合:三行、三列、两条对角线。把这 8 种组合的索引写死,遍历检查即可。
private static final int[][] WIN_LINES = { {0,1,2}, {3,4,5}, {6,7,8}, // 三行 {0,3,6}, {1,4,7}, {2,5,8}, // 三列 {0,4,8}, {2,4,6} // 两条对角线 }; private boolean checkWin(int player) { for (int[] line : WIN_LINES) { if (board[line[0]] == player && board[line[1]] == player && board[line[2]] == player) { return true; } } return false; }这种写法的好处是把「规则」和「逻辑」分离。想改成 4×4 棋盘,只需要改WIN_LINES和数组长度,判定逻辑不用动。很多新手把八个判断写成八个if,改起来痛苦,还容易漏。
isBoardFull就是遍历数组看有没有 0。注意顺序:先判胜负再判平局,否则最后一步既获胜又填满棋盘时,会误判成平局。
4.3 把逻辑和界面接起来
界面层通常是九个Button放在GridLayout里,每个按钮绑定同一个点击监听,通过index区分是哪个格子。
GridLayout grid = findViewById(R.id.grid); for (int i = 0; i < 9; i++) { Button btn = (Button) grid.getChildAt(i); final int index = i; btn.setOnClickListener(v -> { onCellClicked(index); updateUI(); }); }updateUI负责把board数组的状态同步到按钮文字上。空显示空字符串,1 显示「X」,2 显示「O」。这样逻辑和界面就解耦了,测试逻辑时甚至可以脱离 UI 单独跑。
提示:
GridLayout的子 View 顺序和getChildAt的索引一致,但前提是布局里按顺序写。如果用了row/column属性打乱顺序,索引就对不上了,这是常见的翻车点。
5. 避坑与排查:导入、编译、运行中最容易翻车的五件事
前面讲的是顺流程,但实际操作中,十个人有八个会在某些环节卡住。这一章把最常见的五类问题按「现象 → 原因 → 解决」列出来,遇到问题直接对号入座。
5.1 导入后 Gradle 同步失败,报版本不兼容
现象:打开工程后,Build 窗口红字报错,提示 Gradle 版本与 Android Studio 不兼容,或者Could not find method implementation()。
原因:gradle-wrapper.properties里的 Gradle 版本太老,和新版 Android Studio 的 AGP(Android Gradle Plugin)不匹配。或者根目录build.gradle里的插件版本和 Gradle 版本对不上。
解决:打开gradle-wrapper.properties,把distributionUrl改成当前 Android Studio 推荐的版本。同时检查根目录build.gradle里的com.android.tools.build:gradle版本,两者要匹配。改完执行./gradlew clean再同步。
5.2 编译通过但运行闪退,Logcat 报 Resources$NotFoundException
现象:应用装上了,点图标就闪退,Logcat 里看到android.content.res.Resources$NotFoundException。
原因:布局文件里引用了不存在的资源 ID,或者setContentView里的布局名写错。也可能是strings.xml里少了某个字符串,但布局里引用了。
解决:看 Logcat 报错的具体资源名,去res目录里核对。常见的是复制代码时漏了资源文件,或者改了资源名但没改引用。用 Android Studio 的「Analyze → Inspect Code」能扫出一批这类问题。
5.3 模拟器里界面错位,九宫格显示不全
现象:应用能跑,但九宫格挤在一起或者超出屏幕,按钮大小不一致。
原因:GridLayout的行列权重没设好,或者按钮的layout_width/layout_height用了固定值,在不同屏幕密度上表现不一致。
解决:给GridLayout设置android:useDefaultMargins="true",按钮用layout_columnWeight和layout_rowWeight均分空间,宽高用0dp配合权重。这样在不同尺寸屏幕上都能自适应。
5.4 点击格子没反应,或者一次点击落两个子
现象:点按钮没反应,或者点一下出现两个棋子。
原因:监听器绑定了两次,或者onCellClicked里没有先判空。也可能是updateUI在落子前就被调用了。
解决:检查setOnClickListener是否在循环里重复绑定。确认onCellClicked第一行就是if (board[index] != 0) return;。把落子和刷新 UI 的顺序理清:先改数据,再刷界面。
5.5 换电脑后工程打不开,提示 SDK 路径无效
现象:在另一台电脑上打开工程,报SDK location not found。
原因:local.properties文件里记录了本机 SDK 路径,这个文件通常不提交到版本库,换电脑后缺失或路径不对。
解决:在工程根目录新建local.properties,写入sdk.dir=你的SDK路径。Windows 路径里的反斜杠要转义,或者用正斜杠。Android Studio 一般会自动生成这个文件,如果没有,手动补上即可。
注意:
local.properties不要提交到 Git,它和本机环境绑定,提交了别人也用不了。
6. 二次开发与验证:把井字棋改成你自己的课程设计
能跑通、能讲清逻辑之后,课程设计还差一步——让它看起来是你做的。这一步不是让你重写,而是在现有骨架上加自己的东西。加什么、怎么加、怎么验证加对了,这一章给几个具体方向。
6.1 加一个「重新开始」按钮
最简单的扩展是加重新开始功能。在布局里加一个Button,点击时把board数组清零、currentPlayer重置为 1、刷新 UI。
resetBtn.setOnClickListener(v -> { Arrays.fill(board, 0); // 清空棋盘 currentPlayer = 1; // 重置为先手 updateUI(); // 刷新界面 });Arrays.fill是 Java 标准库方法,把数组所有元素设为指定值。比写循环简洁,也不容易出错。加完这个功能,演示时能反复开局,不用重启应用。
6.2 加一个简单的计分板
再进一步,记录 X 和 O 各赢了多少局。用两个int变量存分数,每次判定胜负后对应加一,显示在界面上。
private int scoreX = 0, scoreO = 0; // 在 checkWin 返回 true 后 if (currentPlayer == 1) scoreX++; else scoreO++; scoreText.setText("X: " + scoreX + " O: " + scoreO);计分板的价值在于演示时更有「游戏感」,答辩时也能多讲一个功能点。注意分数不要存在SharedPreferences里除非要求持久化,课程设计一般不需要。
6.3 验证改动是否正确的三个检查点
改完代码别急着交,按这三个检查点过一遍:
| 检查点 | 验证方法 | 通过标准 |
|---|---|---|
| 逻辑正确性 | 手动走三局:X 赢、O 赢、平局 | 三种结果都能正确判定 |
| 状态重置 | 点重新开始后立即落子 | 棋盘清空,先手为 X |
| 计分准确 | 连续对局,核对分数 | 分数与实际胜负一致 |
手动测试比写单元测试快,课程设计场景下够用。如果时间充裕,可以给checkWin写个 JUnit 测试,把八种获胜组合各跑一遍,这样答辩时能说「逻辑有测试覆盖」。
6.4 改包名和界面文案的注意事项
最后一步是改包名和应用名,让它看起来不像模板。包名在app/build.gradle的applicationId和AndroidManifest.xml的package属性里改,两处要一致。应用名在strings.xml的app_name里改。
改包名后,Java 文件里的package声明和import也要跟着改,Android Studio 有「Refactor → Rename」功能可以自动处理。手动改容易漏,漏了编译就报错。
界面文案改strings.xml,别直接在布局里写死中文。这样以后要加多语言支持,只需要加一个values-en/strings.xml就行。
从那以后我每次拿到一份 Android 源码,都强制先跑./gradlew tasks确认构建环境,再导入 IDE 同步,最后才动代码。这个顺序能挡掉八成环境问题,省下的时间够多改两个功能。希望帮到你。
本文还有配套的精品资源,点击获取