前言

在上一篇文章中,我们已经实现了游戏的所有界面和逻辑代码,并且在 Android 上已经可以正常运行。

这篇文章我们将讲解如何将其从使用 jetpack compose 修改为使用 compose-jb 从而实现跨平台。

老规矩,先看效果图:

可以看到,桌面端效果和移动端几乎没有差别,而且在移植过程中几乎没有修改代码,几乎就是直接复制过来就可以用了。

移植过程

准备工作

在开始之前,我们需要换一下 IDE,不再使用 Android Studio 而是改为使用 IntelliJ IDEA 。

其实这里直接使用 Android Studio 也是完全没问题,毕竟 Android Studio 本来就是魔改自 IDEA 社区版的。

而我之所以要换成 IDEA 只是因为 IDEA 的新建项目自带了 Kotlin Multiplatform 模版,而这其中包括了 Compose Multiplatform 模版。

所以我可以直接使用模版创建项目,这样就不用自己建一堆文件夹和文件了。

说简单点其实就是为了偷懒,当然这里说的是完全新建一个跨平台项目,如果你是直接 clone 我的项目或者其他 compose 跨平台项目那就没必要非得用 IDEA。

如果你是新建项目,强烈建议还是使用 IDEA 的模版吧,不然自己手动创建容易出错。

选择如上的项目模版后按照提示一步一步确定即可。

项目包结构

新建好项目后,项目的包结构如图:

其中,根目录下 desktop 、 android 目录分别为安卓和桌面端项目的原生目录。

而 common 则为通用目录,它下面又分了很多目录:

androidMain 目录是安卓的代码(和资源)目录,在编译安卓程序时,其中的代码和资源会被拷贝到根目录下的 android 中。

desktopMain 同理,只不过这个是桌面端目录。

而 commonMain 则是平台无关的通用代码,无论编译的是什么平台都会参与编译。

其他 *Test 是测试代码目录,咱们用不上就先不用管了。

复制代码

知道了各个目录的作用后,我们应该把代码复制到哪儿已经显而易见了。

咱们先把原本项目中的 gameLogic 、 gameView 、 viewModel 三个包中的文件全部复制到 common/src/commonMain/kotlin/包名 目录下,复制完后结构如下:

一般来说不会有什么问题,因为新建项目时使用的模版已经帮我们把导入的依赖改好了。

虽然现在使用的代码没有变,但是实际上导入的包已经不是 jetpack compose 的包了。

如果复制文件过去后有什么问题,按照提示改好即可。

复制资源

由于我们项目中使用到了一些图片,所以需要我们把这些图片分别复制到Android和desktoi的资源目录中:

android 的资源需要复制到 /common/src/androidMain/res 中,因为在安卓中我们使用的是 drawable 资源,所以需要我们在 res 目录中新建一个 drawable 目录,并把资源放到这个目录中,这里其实和原生安卓的资源一样的。

desktop 的资源需要放到 /common/src/desktopMain/resources 目录下。

适配差异代码

其实在写原生安卓程序的时候我们就说过,加载图片的方式安卓和桌面端不一样,所以需要我们单独抽出一个函数,方便现在移植的时候修改。

当时我也以为这可能是这个项目中唯一有差异的地方,没想到复制过来后又发现了两处差异代码,接下来就让我们看看。

首先介绍一下对于平台差异代码应该怎么解决。

我们只需要在 commonMain 中用 expect 声明一个函数,不要写具体实现:

expect fun loadImageBitmap(resourceName: Resource): ImageBitmap

然后分别在 androidMain 和 desktopMain 中实现这个函数:

desktop:

actual fun loadImageBitmap(resourceName: Resource): ImageBitmap {val resPath = when (resourceName) {Resource.WhiteChess -> "white_chess.png"Resource.BlackChess -> "black_chess.png"Resource.Background -> "mood.png"}return useResource(resPath) { androidx.compose.ui.res.loadImageBitmap(it) }
}

android:

@Composable
actual fun loadImageBitmap(resourceName: Resource): ImageBitmap {val resId = when (resourceName) {Resource.WhiteChess -> R.drawable.white_chessResource.BlackChess -> R.drawable.black_chessResource.Background -> R.drawable.mood}return ImageBitmap.imageResource(id = resId)
}

其中的 Resource 是我自己定义的一个枚举类:

enum class Resource {WhiteChess,BlackChess,Background
}

这个枚举类定义了项目中用到的三个资源图片:白子图片、黑子图片、棋盘背景。

对了,为什么我之前的参数类型写的是 String 而现在要改成自定义枚举类,然后在实现中自己去解析?

哈哈,因为我实际写的时候才发现,由于界面代码写在了 commonMain 中,所以是没有 R 这个资源类的,也就是说我没法直接引用资源 ID,仔细想想也是,明明代码是放在平台无关的通用代码中,怎么可能会让使用安卓特有的 R 类呢。

所以,我们界面中加载图片的代码也要对应的改一下:

改之前:

val backgroundImage = loadImageBitmap(resourceName = R.drawable.mood.toString())
val whiteChess = loadImageBitmap(resourceName = R.drawable.white_chess.toString())
val blackChess = loadImageBitmap(resourceName = R.drawable.black_chess.toString())

改之后:

val backgroundImage = loadImageBitmap(resourceName = Resource.Background)
val whiteChess = loadImageBitmap(resourceName = Resource.WhiteChess)
val blackChess = loadImageBitmap(resourceName = Resource.BlackChess)

除此之外,还有一个地方的代码也是需要适配一下,那就是获取屏幕宽度:

val screenWidth = LocalConfiguration.current.screenWidthDp

之前我是万万没想到,这个居然是安卓的特有代码,仔细想想好像确实,这个代码返回的是读取系统配置文件的数据,桌面端确实没有这个东西,而且桌面端的窗口大小是可变的啊。

所以我们需要改一下。

expect: expect fun chessboardSize(): Int

android

@Composable
actual fun chessboardSize(): Int {return LocalConfiguration.current.screenWidthDp
}

desktop

actual fun chessboardSize(): Int {return 300
}

这里因为我们获取屏幕宽度的目的只是为了设置棋盘大小,所以对于桌面端我直接写死了一个值。

最后一个差异代码其实不用适配,但是由于我强迫症,不改总觉得不舒服,所以我还是改了。

那就是 Dialog 这个 composable ,在 jetpack compsoe 中,第一个必须参数的名字是 onDismissRequest 而在 compose-jb 中却叫做 onCloseRequest ……

其实在使用的时候不写参数名就可以不用适配了,但是我感觉不写不舒服,所以就得适配一下了:

expect: expect fun BaseDialog(onCloseRequest: () -> Unit, content: @Composable (() -> Unit))

android

@Composable
actual fun BaseDialog(onCloseRequest: () -> Unit,content: @Composable () -> Unit
) {Dialog(onDismissRequest = onCloseRequest,content = content)
}

desktop

@Composable
actual fun BaseDialog(onCloseRequest: () -> Unit,content: @Composable () -> Unit
) {Dialog(onCloseRequest = onCloseRequest,content = {content()})
}

开始运行!

自此,移植就全部完成了!

我们来看一下运行效果。

桌面端:

在终端中输入: ./gradlew run

或者依次选择 Gradle - desktop - compose desktop - run

移动端:

直接在菜单中运行即可

运行效果:

总结

截止到现在,我们终于完成了所有的界面和逻辑代码,并且成功移植到了 compsoe-jb 实现了跨平台运行。

但是还有亿些小细节需要我们好好的优化一下,这个就留到下一篇文章了,或者如果能写的东西不多的话我就不再写一篇新文章了,我就直接把更新代码提交到 github 得了,所以欢迎大家 star 这个项目。

项目源码地址:reversiChessCompose-Github

跟我一起使用 compose 做一个跨平台的黑白棋游戏(4)移植到compose-jb实现跨平台相关推荐

  1. 跟我一起使用 compose 做一个跨平台的黑白棋游戏(1)整体实现思路

    前言 为什么写这系列文章 虽然 compose 正式版已经出来很久了,也有很多大佬写了很多教程文章和实例 demo ,但是对于 compose 其实我也还是一知半解的. 特别是对于 compose 的 ...

  2. 跟我一起使用 compose 做一个跨平台的黑白棋游戏(2)界面布局

    前言 在上一篇文章中,我们讲解了实现这个游戏的总体思路,这篇文章我们将讲解如何实现游戏界面. 本文将涉及到 compose 的自定义绘制与触摸处理,这些内容都可以在我往期的文章中找到对应的教程,如果对 ...

  3. 用Jetpack Compose做一个俄罗斯方块游戏机

    本文介绍如何使用Jetpack Compose打造一个经典版的俄罗斯方块游戏. 玩过上面这种游戏机的朋友应该会对本文内容感到亲切,废话不多说,先看东西: 1. 为什么Compose适合做游戏? 通常一 ...

  4. 怎样用cocos2d-x做一个基于地图块的游戏(Part One)

    怎样用cocos2d-X做一个基于地图块的游戏 (Part One) 在这个分为上下两部分的教程中,我们将介绍如何使用Cocos2D-X和地图编辑器做一款基于地图块的游戏.在这个简单的地图块游戏里,一 ...

  5. 用pygame做一个简单的python小游戏---贪吃蛇

    用pygame做一个简单的python小游戏-贪吃蛇 贪吃蛇游戏博客链接:(方法一样,语言不一样) c++贪吃蛇:https://blog.csdn.net/weixin_46791942/artic ...

  6. 用pygame做一个简单的python小游戏---七彩同心圆

    用pygame做一个简单的python小游戏-七彩同心圆 这个小游戏原是我同学python课的课后作业,并不是很难,就简单实现了一下,顺便加强一下pygame库的学习. 玩法:每次点击鼠标时,会以鼠标 ...

  7. 用pygame做一个简单的python小游戏---生命游戏

    用pygame做一个简单的python小游戏-生命游戏 生命游戏(Game of Life) 生命游戏(Game of Life)是剑桥大学约翰·何顿·康威(John Horton Conway)教授 ...

  8. python七彩同心圆_用pygame做一个简单的python小游戏---七彩同心圆

    用pygame做一个简单的python小游戏---七彩同心圆 用pygame做一个简单的python小游戏-七彩同心圆 这个小游戏原是我同学python课的课后作业,并不是很难,就简单实现了一下,顺便 ...

  9. 做一个简单的java小游戏--单机版五子棋

    做一个简单的java小游戏–单机版五子棋 学了java有一段时间了,今天就来搞一个简单的单机版五子棋游戏. 实现功能:那必须能进行基础的输赢判断.还有重新开始的功能,悔棋的功能,先手设置的功能和退出的 ...

最新文章

  1. Java关键字(四)——final
  2. Docker镜像制作规范
  3. GDB多线程调试常用命令
  4. flutter --- 使用dio包
  5. 10个经典的爆炸化学反应,个个都是你惹不起的“暴脾气”
  6. 使用Event Message 对 Package 进行Troubleshoot
  7. 8.exchange2013实战操作之RMS
  8. 猫眼top前100电影爬取demo(正则初试)
  9. linux安装php-redis扩展(转)
  10. 网页素材精品:一组五彩缤纷的免费矢量背景素材
  11. 放大电路的分析方法详解
  12. Spec可视化音乐频谱
  13. Android Kotlin关于新增本地数据库对象表字段问题
  14. 免费国外视频素材网站
  15. thinkpad E430拆装与升级
  16. 秦令令:移动营销跨越之年
  17. matlab之拼接图片
  18. 2021南宁周赛!第一期题解
  19. 使用fastjson解析json抓取新浪新闻文章
  20. 职业能力测试试题及答案

热门文章

  1. .zip分卷解压方法
  2. 阿里云 导入 mysql_阿里云服务器怎么导入mysql数据库
  3. 数据库理论 02 SQL——基于《数据库系统概念》第七版
  4. 现阶段有50万资金,想去开一个店,从事什么行业好?
  5. 扫盲科普:工资,税前工资,编制, 正式员工...
  6. 威速(v2), 大IT公司的销售经验, 软件代理机制, 代理协议...
  7. 微信小程序运营系列(四)---门店如何结合小程序进行推广
  8. 单词接龙(搜索与图论、dfs)
  9. c语言编程抢30,C语言编写抢三十游戏——开发笔记(总结).doc
  10. 程序员生存定律[八] 借势的价值和力量