简介:宝可梦图鉴(游戏小程序)是一份面向移动端开发的完整源码资源,适合Android或小程序初学者及宝可梦爱好者用于学习与二次创作。资源包共981个文件,容量仅2.44MB,其中920张png宝可梦图片构成主要素材库,js逻辑脚本负责交互与数据处理,json存储宝可梦名称、属性、种族值等静态信息,wxml/wxss则实现页面结构和样式。下载后可以了解如何搭建图鉴式应用,掌握列表展示、搜索过滤、详情跳转等典型功能,并学习将数据与表现层分离的组织方式。开放源码也为自定义扩展提供基础,可自行增补宝可梦数据或调整界面布局,非常适合作为入门练手项目。目前已有333人学习,整体体量轻巧,适合快速上手与修改调试。
1. 宝可梦图鉴“游戏小程序”源码,拆开全是 JavaScript 数据文件
拿到 pokedex-master 压缩包,第一反应是找 AndroidManifest 和 Activity,结果列表里躺着 spriteList.js、ability.js、type.js、globalobject.js,不少人第一眼以为资源放错了。这套基于 Android 平台的图鉴应用,核心不在界面,而在数据组织方式:上千条宝可梦数据全部脚本化拆分,业务代码围绕数据结构转,界面只是查表渲染。对想学习数据驱动架构的 Android 开发者,以及想把图鉴改造成跨端小程序的独立开发者,这套源码提供了直接的参照——用脚本管理实体数据、技能表和属性克制关系。下面先把这几个数据文件和加载顺序拆清楚,再往列表、详情和踩坑点走。
2. 目录结构与数据模型:spriteList、ability、type 的字段设计
2.1 先分清哪些是运行时代码,哪些是数据文件
压缩包里能直接看到.gitignore、WechatIMG72.jpeg、spriteList.js、ability.js、type.js、index.js、spritelist.js、globalobject.js。其中.js后缀不是服务端脚本,而是打包进 assets 或小程序容器的数据描述文件;WechatIMG72.jpeg是项目文档截图,.gitignore说明源码托管过 Git。完整的 Android 工程一般还会配套 README.md、AndroidManifest.xml、build.gradle 与 src/res 目录,如果手头包内只看到核心脚本,那就按 2.3 的方式把数据层接进新工程。
| 文件 | 作用 | 引用方式 |
|---|---|---|
| globalobject.js | 全局状态与常量定义,数据初始化入口 | 最先加载 |
| spriteList.js / spritelist.js | 宝可梦基础列表:编号、名称、种族值、图片路径 | 列表页数据源 |
| ability.js | 特性数据:名称、效果描述 | 详情页展示 |
| type.js | 属性与克制倍率:火/水/电/草等 | 属性筛选与克制计算 |
| index.js | 页面/模块入口 | require 引入 |
这里最容易翻车的点,是spriteList.js和spritelist.js大小写不一致。如果两个文件同时存在,分不清谁是主数据源,改动一只宝可梦的属性时很容易漏改其中一个,最后表现成“列表页能搜到、详情页查不到”或者“属性筛选结果对不上”。先跑一条命令看引用关系:
grep -rn "spritelist\|spriteList" --include="*.js" . | head -50这条命令里,-rn表示递归查找并输出文件名和行号;双引号中用\|同时匹配spritelist和spriteList两种写法;--include="*.js"限定只查 JS 文件。如果输出里同时出现对两个文件的 require,说明业务代码两处都在引用,数据同步就要盯紧。
2.2 type.js 与 ability.js:克制关系与特性描述
type.js 内部一般是一个对象数组,每个属性带一组克制倍率,结构类似:
// type.js module.exports = [ { name: 'fire', vs: { grass: 2, water: 0.5 } }, { name: 'water', vs: { fire: 2, rock: 2 } } ]这种结构做了两个关键决策:克制关系用数字倍率表达,2 表示克制,0.5 表示抵抗;新属性只需要往数组里追加对象,业务代码不用改。ability.js 的格式类似,保存特性 id、名称和说明文本,详情页按 id 查表渲染。globalobject.js 通常放全局单例,比如已加载数据缓存、当前筛选条件、主题色,页面启动时先执行它。
2.3 加载顺序与首屏策略
在 Android 端,常见做法是把这些 js 文件放进 assets 目录,用 AssetManager 读取字符串,再交给 JS 引擎执行或直接做 JSON 解析。这里有个很隐蔽的问题:globalobject.js 依赖 type.js 和 ability.js 先执行完,如果图省事把所有文件一次性全量 eval,全局变量会被后加载的模块覆盖,宝可梦属性显示成 undefined,且只在特定版本上复现。我一般会在本地先用 Node 验证依赖链:
node -e "require('./globalobject.js'); require('./type.js'); require('./ability.js'); require('./spriteList.js')"按顺序执行一遍,任何模块缺失或引用报错都会直接暴露。注意 require 的加载顺序要和项目里页面的初始化顺序保持一致,否则 Node 跑通了,Android 端依旧踩坑。
提示:如果包内存在两个 index.js 同时导出同一个全局变量,后加载的会覆盖先加载的。排查时先用
grep -rn "globalData"定位所有定义点,再决定保留哪一个。
3. 把脚本数据丢进 RecyclerView:列表、搜索与详情路由
3.1 解析 assets 里的 JS 数据
Android 端先读文件,再交给解析器。但这里有个约束:module.exports = [...]不是合法 JSON,直接new JSONArray(str)会抛异常。我一般会在加载工具里做一个前缀剥离,再走 Gson 映射:
// PokedexDataLoader.java public static String loadAsset(Context ctx, String fileName) { try (InputStream is = ctx.getAssets().open(fileName); BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { sb.append(line).append('\n'); } return sb.toString().replaceFirst("module\\.exports\\s*=\\s*", ""); } catch (IOException e) { return ""; } }replaceFirst只移除第一个赋值标记,避免误伤字符串里出现的module.exports;用 UTF-8 读取是为了保证中文名称不乱码。Gson 映射时有个常见错位:JS 里字段叫sprites,Java/Kotlin 端字段名也必须是sprites,如果改成sprite,解析结果直接为 null,列表图全空。
3.2 列表适配器与 Filter 过滤
列表页用 RecyclerView,数据源就是 spriteList.js 解析后的集合。需要同时支持名称搜索和属性筛选,给适配器实现Filterable是最直接的方案:
class PokedexAdapter(private var items: List<PokedexEntry>) : RecyclerView.Adapter<PokedexAdapter.VH>() { private val full = ArrayList(items) override fun getFilter(): Filter = object : Filter() { override fun performFiltering(chars: CharSequence?): FilterResults { val kw = chars?.toString()?.trim().orEmpty() val filtered = if (kw.isEmpty()) full else full.filter { it.name.contains(kw, true) || it.types.any { t -> t.contains(kw, true) } } return FilterResults().apply { values = filtered } } @Suppress("UNCHECKED_CAST") override fun publishResults(chars: CharSequence?, results: FilterResults?) { items = results?.values as? List<PokedexEntry> ?: emptyList() notifyDataSetChanged() } } }full保存全量数据,performFiltering在后台线程执行,it.name.contains(kw, true)的第二个参数true表示不区分大小写,用户输入pikachu和Pikachu都能命中;it.types.any做属性维度匹配。如果数据量超过三千条,notifyDataSetChanged会造成整表闪烁,这时要换成 DiffUtil 做增量更新。
3.3 列表到详情的传参
列表项点击跳详情时,只传宝可梦编号,不要传整个对象:
startActivity(Intent(this, DetailActivity::class.java).putExtra("pokemon_id", entry.id))详情页拿到 id 后,从内存缓存或 assets 二次查询。原因是图鉴对象字段多,跨页面传对象增加序列化开销,而且详情页被系统回收后重建时无法恢复完整数据。这个思路和迁移到 WebView/小程序容器时保持一致:页面之间只传索引,真实数据统一从数据源读取。
4. 加载页、图片缓存与数据一致性:调优要动的参数
4.1 加载页:不要让图鉴首屏白等
图鉴数据全量加载通常要几百毫秒,Android 上不做处理会在冷启动时出现白屏。常见做法是配置一个 SplashTheme,用 windowBackground 先画一张启动图,同时预加载数据:
<style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowBackground">@drawable/bg_splash</item> </style>class SplashActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { val data = withContext(Dispatchers.IO) { PokedexDataLoader.loadAll(this@SplashActivity) } startActivity(Intent(this@SplashActivity, MainActivity::class.java).putExtra("preloaded", data)) finish() } } }windowBackground让系统在 Activity 创建前一帧就画出背景,不闪白;withContext(Dispatchers.IO)把文件读取切到 IO 线程,避免阻塞主线程。preloaded参数要注意 Binder 传输限制,数据超过 1MB 时改成 MainActivity 内部缓存读取,否则会抛 TransactionTooLargeException。
4.2 图片与列表性能参数
| 性能项 | 常见错误 | 建议值/做法 |
|---|---|---|
| RecyclerView 高度 | wrap_content 反复测量 | 固定 match_parent |
| item 图片 | 原图直接加载 | 1080px 以内,仅缩放不裁剪 |
| 图片加载 | Glide 加载 assets 路径 | file:///android_asset/+ 相对路径 |
| 缓存策略 | 每次进详情都读磁盘 | 启动预加载后使用内存缓存 |
| 分隔线 | 手动设置背景 | 用 ItemDecoration 统一绘制 |
assets 图片加载的标准写法:
Glide.with(imgView) .load(Uri.parse("file:///android_asset/sprites/" + entry.getImagePath())) .override(480, 480) .into(imgView);file:///android_asset/是 assets 目录的固定前缀,路径中不要出现中文文件名,个别机型解析会失败;override(480, 480)把位图缩到 480 边长,减少 GPU 纹理占用,对宝可梦这类小尺寸头像已经足够清晰。
4.3 数据一致性:大小写文件与重复字段
回到 2.1 提到的spriteList.js和spritelist.js。这两个文件并存时,最典型的故障是“列表页正常、详情页查不到数据”。排错顺序:先ls -la确认两个文件都存在,再grep -rln "spritelist\|spriteList"看哪些页面引用了哪个,最后用脚本校验数据唯一性:
// validate.js const fs = require('fs'); const list = JSON.parse( fs.readFileSync('spriteList.js', 'utf8') .replace(/^module\.exports\s*=\s*/, '') ); const ids = list.map(item => item.id); const dup = ids.filter((id, i) => ids.indexOf(id) !== i); console.log(dup.length ? `重复 ID: ${[...new Set(dup)]}` : '无重复');脚本先剥离module.exports前缀,再通过indexOf判断第 i 个 id 是否在更早位置出现过。id 是详情页路由的唯一键,一旦重复,点击列表项可能跳到错误的宝可梦详情,所以校验结果里出现重复 ID 时,优先检查是不是两份 spriteList 的合并逻辑写重复了。
5. 把图鉴数据迁移到小程序容器的验证技巧
5.1 保留数据层、替换渲染层
现在主流跨端方案,包括微信小程序和 UniApp/hBuilderX 构建的小程序,都支持直接加载本地 JS 模块。迁移时最省力的做法是数据层原样搬运:把 spriteList.js、ability.js、type.js 复制到小程序项目的 utils 目录,只重写页面模板和列表组件。需要改动的语法点只有一个,module.exports在小程序里同样可用,但如果走 ES Module 风格,就统一替换成export default。这样 Android 端、小程序端共用一份数据源,改克制倍率或新增宝可梦时只动数据文件,不用双端同步维护。
5.2 一个简单的数据完整性校验
迁移后最先暴露的问题往往是字段名不匹配,Android 端读imagePath,小程序里读的是img。用 Node 写一个字段校验器:
// check-fields.js const fs = require('fs'); const sprite = require('./spriteList.js'); const required = ['id', 'name', 'types', 'img']; const missing = sprite.filter(item => required.some(field => item[field] === undefined) ); console.log(missing.length ? `字段缺失:${missing.map(m => m.id).join(', ')}` : 'OK');required.some(field => item[field] === undefined)只要有一个字段缺失就返回 true,把这条记录筛出来。跑完脚本后统一字段名,比如把两端的img和imagePath全部改成imagePath,不要留兼容分支。更彻底的做法是把它挂进构建流程:在 package.json 里加一条prebuild脚本,执行node check-fields.js || exit 1,任何数据补漏都会在打包前直接失败,而不是等用户点开某只宝可梦才发现页面空白。
本文还有配套的精品资源,点击获取